Wîkîferheng
kuwiktionary
https://ku.wiktionary.org/wiki/Destp%C3%AAk
MediaWiki 1.47.0-wmf.12
case-sensitive
Medya
Taybet
Gotûbêj
Bikarhêner
Gotûbêja bikarhêner
Wîkîferheng
Gotûbêja Wîkîferhengê
Wêne
Gotûbêja wêneyî
MediaWiki
Gotûbêja MediaWiki
Şablon
Gotûbêja şablonê
Alîkarî
Gotûbêja alîkariyê
Kategorî
Gotûbêja kategoriyê
Pêvek
Gotûbêja pêvekê
Nimînok
Gotûbêja nimînokê
Portal
Gotûbêja portalê
Tewandin
Gotûbêja tewandinê
Jinûvesazî
Gotûbêja jinûvesaziyê
TimedText
TimedText talk
Modul
Gotûbêja modulê
Event
Event talk
têr bûn
0
6637
6277052
5891985
2026-07-24T02:22:47Z
Ghybu
1393
/* Lêker */
6277052
wikitext
text/x-wiki
{{bnr-n|têrbûn}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-têr bûn.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Lêker ===
{{ku-tewîn-lk|têr bûn|form=negerguhêz|niha=b|borî=bû|pêşgir=têr|valahî=erê}}
{{lêker|ku|c=h-ngh}}
# [[tijî bûn]], [[mişt]] bûn, [[dagirtî]] bûn, ji [[tişt]]ekî [[bes]] bûn
#* {{bêmînak|ku}}
==== Bi alfabeyên din ====
* {{ku-ar|تێر بوون}}
===== Binêre herwiha ====
* {{g|ku|têr xwarin}}
==== Etîmolojî ====
Ji {{heve|ku|têr|bûn}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W-|de|genug bekommen}}, {{W-|de|satt sein}}
* {{Z|ar}}: {{W-|ar|إشباع}}
* {{Z|fa}}: {{W-|fa|سیر بودن}}, {{W-|fa|سیر شدن}}
* {{Z|en}}: to {{W-|en|be full}}, to {{W-|en|be satisfied}}, {{W+|en|thicken}}
* {{Z|tr}}: {{W+|tr|doymak}}, {{W+|tr|doyunmak}}, {{W+|tr|kanmak}}, {{W+|tr|tatmin olmak}}, {{W+|tr|doyulmak|guman=erê}}, {{W-|tr|bağdat'ı tamir etmek|guman=erê}}
{{werger-bin}}
flf4zvhsb7h1bk93kr857b6wudabfk8
6277053
6277052
2026-07-24T02:23:01Z
Ghybu
1393
/* = Binêre herwiha */
6277053
wikitext
text/x-wiki
{{bnr-n|têrbûn}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-têr bûn.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Lêker ===
{{ku-tewîn-lk|têr bûn|form=negerguhêz|niha=b|borî=bû|pêşgir=têr|valahî=erê}}
{{lêker|ku|c=h-ngh}}
# [[tijî bûn]], [[mişt]] bûn, [[dagirtî]] bûn, ji [[tişt]]ekî [[bes]] bûn
#* {{bêmînak|ku}}
==== Bi alfabeyên din ====
* {{ku-ar|تێر بوون}}
==== Binêre herwiha ====
* {{g|ku|têr xwarin}}
==== Etîmolojî ====
Ji {{heve|ku|têr|bûn}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W-|de|genug bekommen}}, {{W-|de|satt sein}}
* {{Z|ar}}: {{W-|ar|إشباع}}
* {{Z|fa}}: {{W-|fa|سیر بودن}}, {{W-|fa|سیر شدن}}
* {{Z|en}}: to {{W-|en|be full}}, to {{W-|en|be satisfied}}, {{W+|en|thicken}}
* {{Z|tr}}: {{W+|tr|doymak}}, {{W+|tr|doyunmak}}, {{W+|tr|kanmak}}, {{W+|tr|tatmin olmak}}, {{W+|tr|doyulmak|guman=erê}}, {{W-|tr|bağdat'ı tamir etmek|guman=erê}}
{{werger-bin}}
3ago70mldjluvdgfq9rk4bwdi40vnau
têr
0
6641
6277056
6253245
2026-07-24T02:25:00Z
Ghybu
1393
/* Jê */
6277056
wikitext
text/x-wiki
{{bnr-h|ter|terr|têrr|Ter}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{beş|Rengdêr}} {{ku-IPA}} {{r-ya nerm}}, تێر
:* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-têr.wav|Deng|Bidlîs}}
* {{beş|Navdêr 1}} {{ku-IPA|têř}}, {{r-ya req}}, تێڕ
{{beş|Navdêr 2 û 3}} {{ku-IPA|tˁêr}} {{t-ya stûr}}, طێر
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}, {{t-ya req}}, {{r-ya nerm}}
# Ya / yê [[êdî]] ne [[hewce|hewceyî]] tiştekê/î (bi taybetî [[xwarin]]ê).
#* {{jêder|ku|jêgirtin=Hespên cehxur yên '''têr''', bezand.|{{Jêgirtin/Egîdê Xudo/Dê û Dêmarî}}}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Yek bi yek peykan çevî bûn çûne cergê da kulab</br>Pir bi wan curhan xwe '''têr''' in lew ko dî derman dikir</br>Şerbet û ava heyatê şekker û qend û gulab}}
==== Nêzîk ====
* {{g|ku|bes}}
* {{g|ku|kafî}}
==== Dijmane ====
* {{g|ku|birçî}}
==== Bide ber ====
* {{g|ku|hewce}}
==== Jê ====
{{kol3|ku|cure=Jê
|têr bûn<pos:lêker>
|têr xwarin<pos:lêker>
|têr kirin<pos:lêker>
|[[têr kirin|têra/î (kesekê/î yan tiştekê/î) kirin]]<w:bo kesekî [[bes]] anku [[kafî]] bûn>
|têrbûn<pos:navdêr>
|têrxwarin<pos:navdêr>
|têrkirin<pos:navdêr>
|têrî
|têrîtî
|têretêr
|têrdan
}}
==== Etîmolojî ====
Têkilî peyva {{hv|fa|سیر|sîr}}, bi yeqînî nayê zanîn.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|vol}}
* {{Z|de}}: {{W+|de|satt}}, {{W+|de|voll}}
* {{Z|aii}}: {{W-|aii|ܣܒ݂ܝܼܥܵܐ|n|tr=swīʿa}}
* {{Z|rup}}: {{W-|rup|sãtul}}
* {{Z|az}}: {{W+|az|tox}}, {{W-|az|doyğun}}, {{W+|az|doymuş}}
* {{Z|ba}}: {{W-|ba|туҡ}}, {{W-|ba|туйған}}
* {{Z|be}}: {{W-|be|сы́ты|n}}
* {{Z|bcl}}: {{W-|bcl|basog}}
* {{Z|bg}}: {{W+|bg|сит}}, {{W+|bg|нахранен}}
* {{Z|my}}: {{W+|my|ဝ}}
* {{Z|cs}}: {{W+|cs|plný}}, {{W+|cs|sytý}}, {{W-|cs|nasycený}}, {{W-|cs|zasycený}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|飽|tr=baau2}}
*: {{Z|cmn}}: {{W+|cmn|飽|tr=bǎo}}
* {{Z|dlm}}: {{W-|dlm|satoil}}
* {{Z|da}}: {{W+|da|mæt}}, {{W-|da|fyldt op}}
* {{Z|id}}: {{W+|id|kenyang}}
* {{Z|ar}}: {{W-|ar|شِبَع|n}}
*: {{Z|acw}}: {{W-|acw|شبعان|tr=šabʿān}}
* {{Z|hy}}: {{W+|hy|կուշտ}}
* {{Z|eo}}: {{W+|eo|sata}}
* {{Z|fa}}: {{W+|fa|سیر|tr=sir, sêr}}
* {{Z|fo}}: {{W-|fo|mettur}}
* {{Z|fi}}: {{W+|fi|täynnä}}, {{W+|fi|kylläinen}}
* {{Z|fr}}: {{W+|fr|rassasier|cuda=rassasié}}, {{W+|fr|repaitre|cuda=repu}}
* {{Z|gd}}: {{W-|gd|làn}}, {{W-|gd|lìonta}}
* {{Z|gl}}: {{W-|gl|cheo|n}}, {{W-|gl|satisfeito}}
* {{Z|got}}: {{W-|got|𐍃𐌰𐌸𐍃}}
* {{Z|ka}}: {{W-|ka|მაძღარი}}
* {{Z|nl}}: {{W+|nl|vol}}, {{W+|nl|verzadigd}}, {{W+|nl|zat}}
* {{Z|ang}}: {{W-|ang|satur}}
* {{Z|ang}}: {{W-|ang|full}}
* {{Z|enm}}: {{W-|enm|sad}}, {{W-|enm|ful}}
* {{Z|en}}: {{W+|en|full}} (satisfied, in relation to eating)
* {{Z|it}}: {{W+|it|sazio}}, {{W+|it|pieno}}, {{W+|it|satollo}}
* {{Z|is}}: {{W-|is|mettur}}
* {{Z|ja}}: {{W+|ja|一杯|tr=いっぱい, ippai}}
* {{Z|pam}}: {{W-|pam|mabsi}}
* {{Z|kea}}: {{W-|kea|farta}}
* {{Z|ca}}: {{W+|ca|ple}}, {{W+|ca|tip}}
* {{Z|ko}}: {{W-|ko|부르-다}}
* {{Z|lo}}: {{W-|lo|ອິ່ມ}}
* {{Z|mk}}: {{W-|mk|сит}}
* {{Z|ms}}: {{W-|ms|kenyang}}
* {{Z|gv}}: {{W-|gv|lane}}
* {{Z|mi}}: {{W-|mi|whiu}}
* {{Z|no}}: {{W+|no|mett}}
* {{Z|ps}}: {{W+|ps|موړ|tr=moṛ}}, {{W+|ps|ډک|tr=ḍak}}
* {{Z|pl}}: {{W+|pl|syty}}
* {{Z|pt}}: {{W+|pt|cheio}}, {{W+|pt|satisfeito}}
* {{Z|ro}}: {{W+|ro|săturat}}, {{W+|ro|sătul}}
* {{Z|ru}}: {{W+|ru|сы́тый}}, {{W+|ru|нае́вшийся}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|си̏т}}
*: Latînî: {{W+|sh|sȉt}}
* {{Z|cu}}: {{W-|cu|сꙑтъ}}
* {{Z|sk}}: {{W-|sk|sýty}}
* {{Z|sl}}: {{W-|sl|sit}}
* {{Z|es}}: {{W+|es|satisfecho}}, {{W+|es|lleno}}
* {{Z|su}}: {{W-|su|wareg}}
* {{Z|sv}}: {{W+|sv|mätt}}
* {{Z|tl}}: {{W+|tl|busog}}
* {{Z|nod}}: {{W-|nod|กั้ดต๊อง}}, {{W-|nod|กั้ดต้อง}}
* {{Z|th}}: {{W+|th|อิ่ม}}
* {{Z|te}}: {{W+|te|సంతృప్తి}}
* {{Z|tr}}: {{W+|tr|tok}}
* {{Z|uk}}: {{W-|uk|си́тий}}
* {{Z|vi}}: {{W+|vi|no}}, {{W+|vi|no nê}}, {{W-|vi|no bụng}}
* {{Z|km}}: {{W+|km|ឆ្អែត|tr=c’aet}}
* {{Z|el}}: {{W+|el|χορτάτος|n}}, {{W+|el|χορτασμένος|n}}
{{werger-bin}}
:{{werger çavkanî|en|full|beş=Ev qismê Wergerê|oldid=75708434}}
=== Navdêr 1 ===
{{navdêr|ku|z=m}}
# [[kafî]], [[bes]]
#: ''Yek '''têra''' min dike.''
#:: Yek bo min bes e.
==== Werger ====
{{werger-ser}}
{{werger-bin}}
=== Navdêr 2 ===
{{ku-tewîn-nav|têr|mê}}
{{navdêr|ku|z=m}}, {{r-ya req}}
# {{maneid|ku|xurc}} [[çante|Çante]]yek mezin e tiştan dikin têde û li pişta [[dewar]]an dikin (nemaze tûrikên li pişta keran ji bo kel û pelên rewiyê bi kerê an jibo şivanan).
#: {{hevmaneyên peyvê|ku|heqîb|xurc}}
# {{maneid|ku|zik}} ''[[peyv]]ek [[tal]] e li şûna'' '''[[zik]]'''
==== Formên alternatîv ====
* {{g|ku|têrr}} (2)
==== Bide ber ====
* {{g|ku|hefsar}}
* {{g|ku|kurtan}}
* {{g|ku|parzûn}}
* {{g|ku|rikêb}}
* {{g|ku|sercil}}
* {{g|ku|zîn}}
==== Jê ====
* {{g|ku|ziktêr}}
* {{g|ku|ziktêrî}}
==== Etîmolojî ====
{{bêetîmolojî}}
==== Werger ====
{{werger-ser|çante}}
* {{Z|de}}: {{W+|de|Satteltasche|m}}
* {{Z|bg}}: {{W+|bg|дисаги|m-p}}
* {{Z|ar}}:
*: {{Z|arz}}: {{W-|arz|خرج|n|tr=ḵurg}}
* {{Z|fa}}: {{W+|fa|خرجین|tr=xorjin}}
* {{Z|fi}}: {{W-|fi|satulalaukku}}
* {{Z|fr}}: {{W+|fr|sacoche|m}}
* {{Z|nl}}: {{W+|nl|zadeltas|m}}
* {{Z|en}}: {{W+|en|saddlebag}}, {{W+|en|alforja}}
* {{Z|ga}}: {{W-|ga|mála diallaite|n}}
* {{Z|ca}}: {{W+|ca|alforja|m}}
* {{Z|la}}: {{W-|la|averta|m}}
* {{Z|lb}}: {{W-|lb|Posch|m}}
* {{Z|mk}}: {{W-|mk|дисаѓи|m-p}}
* {{Z|mi}}: {{W-|mi|terapēke}}
* {{Z|no}}: {{W-|no|saltaske|m}}, {{W-|no|sykkelveske|m}}
* {{Z|pl}}: {{W+|pl|juk|n}}
* {{Z|ru}}: {{W+|ru|перемётная сума́|m}}, {{W-|ru|седе́льный вьюк|n}}
* {{Z|es}}: {{W+|es|alforja|m}}
* {{Z|tl}}: {{W-|tl|kabalyas}}
* {{Z|tr}}: {{W+|tr|harar}}, {{W+|tr|çuval}},
* {{Z|bo}}: {{W-|bo|རྟ་སྒྲོ}}
* {{Z|cy}}: {{W-|cy|bag cyfrwy|n}}
{{werger-bin}}
:{{werger çavkanî|en|saddlebag|beş=Ev qismê Wergerê|oldid=73264967}}
{{werger-bin}}
{{werger-ser|peyvek tal li şûna zik}}
{{werger-bin}}
=== Navdêr 3 ===
{{navdêr|ku|z=n}}
# {{f|ku|balinde}} Cureyek [[firinde]]yan e mina [[kûnd]]an {{g|ku|goştxûr}} e.
# {{f|ku|buxik}} Kêzek xweşik û rengîn e û difire.
#: {{hevmaneyên peyvê|ku|belbelîtank|pelîtank}}
==== Etîmolojî ====
Ihtimalen ji peyva ''[[teyr]]''.
==== Werger ====
{{werger-ser|balinde|1}}
{{werger-bin}}
{{werger-ser|kêz|2}}
* {{Z|ar}}: {{W+|ar|فَرَاشَة}}
* {{Z|en}}: {{W+|en|butterfly}}
{{werger-bin}}
=== Navdêr 4 ===
{{navdêr|ku|z=n}}
# {{guharto|ku|teyr|w=heywanên ku difirrin}}
rpufvas4jgd444o76dls7vtrrrs0uqf
gewre
0
6745
6276937
6129775
2026-07-23T13:18:09Z
Memo1341
14691
/* Etîmolojî */
6276937
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# [[mezin]], [[gir]], [[zexm]], [[girs]], [[giregir]]
#* {{bêmînak|ku}}
==== Jê ====
{{kol2|ku|cure=Jê
|gewre kirin<q:lêker>
|gewrekirin<q:navdêr>
|gewrekirî<q:rengdêr>
|gewreyane
|gewretî
|gewreyî
}}
==== Etîmolojî ====
Ji {{ji|arc|ku|ܓܒܪܐ|tr=gaḇrā}}, [[hevreh]]a {{hv|he|גֶּבֶר|tr=géver, geḇer}}, {{hv|ar|جَبْر}}, {{hv|gez|ገብር}}... Heman peyva aramî herwiha serekaniya peyva ''[[gawir]]'' e jî.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W|de|voluminös|guman=erê}}
* {{Z|fa}}: {{W|fa|باعظمت}}, {{W|fa|بزرگ}}, {{W|fa|کبیر}}, {{W|fa|کلان|guman=erê}}
* {{Z|en}}: {{W|en|ample}}, {{W|en|jumbo}}, {{W|en|grand}}, {{W|en|capital}}, {{W|en|bulky}}, {{W|en|considerable}}, {{W|en|greasy}}, {{W|en|massive}}, {{W|en|sizable}}, {{W|en|stately}}, {{W|en|tremendous}}, {{W|en|voluminous}}, {{W|en|senior citizen|guman=erê}}, {{W|en|sweeping|guman=erê}}, {{W|en|big|guman=erê}}, {{W|en|jaguarundi|guman=erê}}
* {{Z|tr}}: {{W|tr|azametli}}, {{W|tr|âli}}, {{W|tr|celil}}, {{W|tr|cesim}}, {{W|tr|grimsi}}, {{W|tr|kebir}}, {{W|tr|şanlı}}, {{W|tr|ulu}}, {{W|tr|koskoca}}, {{W|tr|ulvî}}, {{W|tr|büyük}}, {{W|tr|iri}}, {{W|tr|muazzam}}, {{W|tr|kocaman}}, {{W|tr|koca|guman=erê}}
{{werger-bin}}
mti0cfq7y351cvx2efdlww7gf9g72v3
6276945
6276937
2026-07-23T18:32:51Z
Wikihez
7344
Reverted 1 edit by [[Special:Contributions/Memo1341|Memo1341]] ([[User talk:Memo1341|talk]]): Chyet gewre tenê bi soranî dide ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6276945
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# [[mezin]], [[gir]], [[zexm]], [[girs]], [[giregir]]
#* {{bêmînak|ku}}
==== Jê ====
{{kol2|ku|cure=Jê
|gewre kirin<q:lêker>
|gewrekirin<q:navdêr>
|gewrekirî<q:rengdêr>
|gewreyane
|gewretî
|gewreyî
}}
==== Etîmolojî ====
Vê dawiyê bi riya {{deyn|ckb|ku|گەورە}} ji {{ji|arc|ku|ܓܒܪܐ|tr=gaḇrā}}, [[hevreh]]a {{hv|he|גֶּבֶר|tr=géver, geḇer}}, {{hv|ar|جَبْر}}, {{hv|gez|ገብር}}... Heman peyva aramî herwiha serekaniya peyva ''[[gawir]]'' e jî.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W|de|voluminös|guman=erê}}
* {{Z|fa}}: {{W|fa|باعظمت}}, {{W|fa|بزرگ}}, {{W|fa|کبیر}}, {{W|fa|کلان|guman=erê}}
* {{Z|en}}: {{W|en|ample}}, {{W|en|jumbo}}, {{W|en|grand}}, {{W|en|capital}}, {{W|en|bulky}}, {{W|en|considerable}}, {{W|en|greasy}}, {{W|en|massive}}, {{W|en|sizable}}, {{W|en|stately}}, {{W|en|tremendous}}, {{W|en|voluminous}}, {{W|en|senior citizen|guman=erê}}, {{W|en|sweeping|guman=erê}}, {{W|en|big|guman=erê}}, {{W|en|jaguarundi|guman=erê}}
* {{Z|tr}}: {{W|tr|azametli}}, {{W|tr|âli}}, {{W|tr|celil}}, {{W|tr|cesim}}, {{W|tr|grimsi}}, {{W|tr|kebir}}, {{W|tr|şanlı}}, {{W|tr|ulu}}, {{W|tr|koskoca}}, {{W|tr|ulvî}}, {{W|tr|büyük}}, {{W|tr|iri}}, {{W|tr|muazzam}}, {{W|tr|kocaman}}, {{W|tr|koca|guman=erê}}
{{werger-bin}}
8dg8f1fmvy4v50inpiqz4vakhnyt8m8
6276948
6276945
2026-07-23T18:38:28Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6276948
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# [[mezin]], [[gir]], [[zexm]], [[girs]], [[giregir]]
#* {{bêmînak|ku}}
==== Jê ====
{{kol2|ku|cure=Jê
|gewre kirin<q:lêker>
|gewrekirin<q:navdêr>
|gewrekirî<q:rengdêr>
|gewreyane
|gewretî
|gewreyî
}}
==== Etîmolojî ====
Vê dawiyê bi riya {{deyn|ckb|ku|گەورە}} ji {{ji|arc|ku|ܓܒܪܐ|tr=gaḇrā}}, [[hevreh]]a {{hv|he|גֶּבֶר|tr=géver, geḇer}}, {{hv|ar|جَبْر}}, {{hv|gez|ገብር}}... Heman peyva aramî herwiha serekaniya peyva {{cotreh|ku|gawir|nivîs=na}} e jî.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W|de|voluminös|guman=erê}}
* {{Z|fa}}: {{W|fa|باعظمت}}, {{W|fa|بزرگ}}, {{W|fa|کبیر}}, {{W|fa|کلان|guman=erê}}
* {{Z|en}}: {{W|en|ample}}, {{W|en|jumbo}}, {{W|en|grand}}, {{W|en|capital}}, {{W|en|bulky}}, {{W|en|considerable}}, {{W|en|greasy}}, {{W|en|massive}}, {{W|en|sizable}}, {{W|en|stately}}, {{W|en|tremendous}}, {{W|en|voluminous}}, {{W|en|senior citizen|guman=erê}}, {{W|en|sweeping|guman=erê}}, {{W|en|big|guman=erê}}, {{W|en|jaguarundi|guman=erê}}
* {{Z|tr}}: {{W|tr|azametli}}, {{W|tr|âli}}, {{W|tr|celil}}, {{W|tr|cesim}}, {{W|tr|grimsi}}, {{W|tr|kebir}}, {{W|tr|şanlı}}, {{W|tr|ulu}}, {{W|tr|koskoca}}, {{W|tr|ulvî}}, {{W|tr|büyük}}, {{W|tr|iri}}, {{W|tr|muazzam}}, {{W|tr|kocaman}}, {{W|tr|koca|guman=erê}}
{{werger-bin}}
2qz1swdr9i92wa8firldu0j5twh7h8l
hindir
0
10326
6277048
6262420
2026-07-24T02:08:42Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6277048
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-hundir.wav|nivîs=hundir / hindur|dever=Amed}}
* {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-hindir.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|hindir|nêr}}
[[Wêne:Bunny in box.jpg|thumb|[[xirnîfk|Xirnîfk]]ek di '''hindirê''' [[karton]]ê de.]]
{{navdêr|ku|z=n}}
# [[tê ve]], [[tê de]], [[daxil]], ne [[derve]], ne [[der]], ne [[xaric]]
#: ''Kerem kin, werin '''hindir'''.''
#: ''Hemî li '''hindir''' rûniştî bûn.''
#: ''Zû ji '''hindir''' derket ser cadeyê.''
#: '''''hindirê''' xanî''
# [[navxwe]]
#* {{bêmînak|ku}}
# {{e|ku|li|Efrîn}} [[ode]]
#: ''Sê '''hindirên''' me hene.''
==== Bi alfabeyên din====
* {{ku-ar|هندڕ}}
==== Formên alternatîv ====
{{kol3|ku|cure=Herwiha
| {{hw|ku|hendir}}
| {{hw|ku|hendirr}}
| {{hw|ku|hindirr}}
| {{hw|ku|hindirû}}
| {{hw|ku|hindirû}}
| {{hw|ku|hundir}}
| {{hw|ku|hundirr}}
| {{hw|ku|hundirû}}
| {{hw|ku|hundirrû}}
| {{hw|ku|hindur}}
| {{hw|ku|hindurr}}
| {{hw|ku|hindurû}}
| {{hw|ku|hindurrû}}
| {{hw|ku|hundur}}
| {{hw|ku|hundurr}}
| {{hw|ku|hundurû}}
| {{hw|ku|hundurrû}}
| {{hw|ku|hûndir}}
| {{hw|ku|windir}}
| {{hw|ku|windirr}}
| {{hw|ku|windirû}}
| {{hw|ku|windirrû}}
| {{hw|ku|wundir}}
| {{hw|ku|wundirr}}
| {{hw|ku|wundirû}}
| {{hw|ku|wundirrû}}
| {{hw|ku|windur}}
| {{hw|ku|windurr}}
| {{hw|ku|windurû}}
| {{hw|ku|windurrû}}
| {{hw|ku|wundur}}
| {{hw|ku|wundurr}}
| {{hw|ku|wundurû}}
| {{hw|ku|wundurrû}}
}}
==== Jê ====
* {{g|ku|hindirî}}
* {{g|ku|hindirîn}}
==== Etîmolojî ====
Ji {{ji|ine-pro|ku|*h₁énteros}}, hevreha kurmancî [[hinav]], ên {{hv|fa|اندر|tr=ender|w=hindir, tê de}} û {{g|fa|اندرون|tr=enderûn|w=[[hinav]]}} {{hv|sa|अन्तर|tr=entere}}, {{hv|grc|ἔντερον|tr=enderon|w=[[hinav]]}}, {{hv|la|intra}} û {{q|la|interior}}, {{hv|en|enter}} (ji latînî).
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W|de|innerlich}}, {{W|de|intern}}, {{W|de|inwendig}}, {{W|de|drinnen}}, {{W|de|Innenraum|guman=erê}}
* {{Z|cs}}: {{W|cs|vnitřní}}
* {{Z|da}}: {{W|da|indre}}
* {{Z|ar}}: {{W|ar|بطن|guman=erê}}, {{W|ar|داخل|guman=erê}}
* {{Z|eo}}: {{W|eo|interna}}
* {{Z|fa}}: {{W|fa|اندر}}, {{W|fa|درون}}, {{W|fa|داخل}}
* {{Z|fo}}: {{W|fo|insti}}, {{W|fo|innan-}}
* {{Z|fi}}: {{W|fi|sisäinen}}
* {{Z|fr}}: {{W|fr|intérieur}}
* {{Z|nl}}: {{W|nl|binnenste}}, {{W|nl|binnenlands}}, {{W|nl|intern}}, {{W|nl|inwendig}}
* {{Z|io}}: {{W|io|interna}}
* {{Z|en}}: {{W|en|in}}, {{W|en|inside}}, {{W|en|inner}}, {{W|en|internal}}, {{W|en|endogenous}}, {{W|en|inland}}, {{W|en|interior}}, {{W|en|intra-}}, {{W|en|inward}}, {{W|en|ento-}}, {{W|en|into}}, {{W|en|indoor|guman=erê}}, {{W|en|inwards|guman=erê}}
* {{Z|it}}: {{W|it|interno}}
* {{Z|ca}}: {{W|ca|interior}}, {{W|ca|intern}}
* {{Z|pap}}: {{W-|pap|interno}}
* {{Z|pt}}: {{W|pt|interior}}, {{W|pt|interno}}
* {{Z|ru}}: {{W|ru|внутренный}}
* {{Z|es}}: {{W|es|interior}}, {{W|es|interno}}
* {{Z|sv}}: {{W+|sv|insida}}
* {{Z|tr}}: {{W|tr|iç}}, {{W|tr|içinde}}, {{W|tr|dahil}}, {{W|tr|derun}}, {{W|tr|zamir}}, {{W|tr|içi}}, {{W|tr|içeri}}
{{werger-bin}}
s951wgevjabf7vxjh8vlmvqush81lmh
hindur
0
10333
6277043
4419387
2026-07-24T01:58:16Z
Ghybu
1393
/* Navdêr */
6277043
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# {{guherto|hindir}}
#* {{jêder-pirtûk|ku|jêgirtin=Çil text di '''hindurê''' miẍarê de danî ne . Lawik kete bin textekî û xwe veşart.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (ÇÎROKA QUNDIRÊ)|cih=Parîs|sal=1940|rûpel=166}}
lw5rkov55z69tvx6cf78msy9mjraqm0
6277058
6277043
2026-07-24T02:36:40Z
Ghybu
1393
/* Navdêr */
6277058
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# {{guherto|hindir}}
#* {{jêder-pirtûk|ku|jêgirtin=Çil text di '''hindurê''' miẍarê de danî ne . Lawik kete bin textekî û xwe veşart.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (ÇÎROKA QUNDIRÊ)|cih=Parîs|sal=1940|rûpel=10}}
06jqp25oeoswwqdjteq6tv9xkzp0xvs
text
0
15050
6277046
6262411
2026-07-24T02:05:41Z
Ghybu
1393
/* Navdêr */
6277046
wikitext
text/x-wiki
{{bnr-h|Text}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|text|nêr}}
[[Wêne:Vrba postelja.jpg|thumb|text (1)]]
[[Wêne:Polish throne at Warsaw Royal Castle.PNG|thumb|text (2)]]
{{navdêr|ku|z=n}}, {{t-ya nerm}} (1,2)
# [[dar]]ê [[nivîn]] li ser tên [[bi cih kirin]] û mirov li ser [[nivistin|dinive]] anku [[razan|radize]] anku [[raketin|radikeve]]
#: {{mk|ku|Ez li ser '''text''' nanivim, li erdê li ser doşekê baştir xewa min tê.}}
#* {{jêder-pirtûk|ku|jêgirtin=Çil '''text''' di hindurê miẍarê de danî ne . Lawik kete bin '''textekî''' û xwe veşart.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (ÇÎROKA QUNDIRÊ)|cih=Parîs|sal=1940|rûpel=166}}
# [[erş]], [[kursî|kursiya]] [[fermî]] ya [[padişah]]î
#: ''[[paytext]]''
# [[texte]], [[dar]], [[dep]], [[dirext]]
#* {{bêmînak|ku}}
==== Ji wêjeya klasîk ====
* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Dîsa ciwan bû min di rext</br>Ew lami'a dayî ji '''text'''</br>Ma kane berqen xulleba}}
==== Bi alfabeyên din ====
* {{ku-ar|تهخت}}
==== Formên alternatîv ====
* [[taxt]]
==== Etîmolojî ====
Têkildarî peyva ''[[dirext]]'' anku ''[[dar]]'', [[hevreh]]a {{g|fa|تخت|tr=text}}, pehlewî ''text, taxt'', ji {{ji|ira|ku|-}}.
==== Jê ====
* [[textî]]
* [[textîn]]
* [[textînî]]
==== Werger ====
'''1. nivîn''':
{{werger-ser}}
* {{Z|af}}: {{W+|af|bed}}
* {{Z|sq}}: {{W+|sq|krevat}} {{n}} ; {{W+|sq|shtrat}}, {{W-|sq|shrati}}
* {{Z|de}}: {{W+|de|Brett}} {{nt}}, {{W+|de|Bohle}} {{m}}, {{W+|de|Bett}} {{nt}}, {{W+|de|Bettstelle}}, {{W+|de|Bettgestell}} {{nt}}, {{W+|de|Falle}} {{m}}, {{W+|de|Holzstück}}, {{W+|de|Podest}}, {{W+|de|Tafel}} {{m}}
* {{Z|bm}}: {{W-|bm|glan}}
* {{Z|eu}}: {{W+|eu|ohe}}
* {{Z|bn}}: {{W+|bn|বিছানা|R=}}
* {{Z|bi}}: {{W-|bi|bed}}
* {{Z|br}}: {{W+|br|gwele}} {{n}}
* {{Z|my}}: {{W-|my|အိပ္ရာ}} (ʔeʔya)
* {{Z|cs}}: {{W+|cs|postel}}
* {{Z|zh}}: {{W+|zh|床|R=chuáng}}
* {{Z|da}}: {{W+|da|seng}}
* {{Z|dog}}: {{W-|dog|damm}}
* {{Z|id}}: {{W-|id|tempat tidur}}, {{W+|id|ranjang}}
* {{Z|ar}}: {{W-|ar|الكرسي|guman=erê}}, {{W+|ar|خشب|guman=erê}},{{W+|ar|سرير}}
* {{Z|hy}}: {{W+|hy|մահճակալ|R=mahč̣akal}}
* {{Z|eo}}: {{W+|eo|lito}}
* {{Z|fa}}: {{W+|fa|بستر|tr=bister}}, {{W-|fa|تخت حکمرانی|guman=erê}}, {{W-|fa|یاتاق|guman=erê}}
* {{Z|fo}}: {{W-|fo|song}}
* {{Z|fi}}: {{W+|fi|vuode}}
* {{Z|fr}}: {{W+|fr|lit|n}}
* {{Z|fy}}: {{W-|fy|bêd}}
* {{Z|gu}}: {{W-|gu|બિછાનું}} ; {{W+|gu|શય્યા}}
* {{Z|hi}}: {{W+|hi|बिस्तर|R=bistar}}
* {{Z|nl}}: {{W+|nl|bed}} {{nt}}, {{W+|nl|legerstede}}, {{W+|nl|sponde}}
* {{Z|io}}: {{W+|io|lito}}
* {{Z|ang}}: {{W+|ang|bed}}, {{W+|ang|bedd}}
* {{Z|en}}: {{W+|en|bed}}, {{W+|en|batten|guman=erê}}, {{W+|en|couch|guman=erê}}, {{W+|en|divan|guman=erê}}, {{W-|en|fashion-plate|guman=erê}}, {{W+|en|hylo-|guman=erê}}, {{W+|en|lair|guman=erê}}, {{W+|en|lign-|guman=erê}}, {{W+|en|settee|guman=erê}}, {{W+|en|tablet|guman=erê}}, {{W+|en|throne|guman=erê}}, {{W+|en|wash basin|guman=erê}}, {{W+|en|washbasin|guman=erê}}, {{W+|en|washbowl|guman=erê}}, {{W+|en|wood|guman=erê}}
* {{Z|iu}}: {{W+|iu|ᐃᒡᓕᖅ|R=[[igliq]]}}
* {{Z|it}}: {{W+|it|letto}} {{n}}
* {{Z|is}}: {{W-|is|beð}} {{nt}} ; {{W+|is|rúm}}
* {{Z|ja}}: {{W-|ja|ベッド, beddo}}
* {{Z|ca}}: {{W+|ca|llit}}
* {{Z|ko}}: {{W+|ko|침대|R=chimdae}}
* {{Z|kw}}: {{W-|kw|gwely}}
* {{Z|lv}}: {{W-|lv|gulta}}
* {{Z|lt}}: {{W+|lt|lova}}; {{W-|lt|guolis}}
* {{Z|ms}}: {{W+|ms|katil}}
* {{Z|mr}}: {{W-|mr|बिछाना|R=}}
* {{Z|hu}}: {{W+|hu|ágy}}
* {{Z|no}}: {{W+|no|seng}} {{n}}
* {{Z|pap}}: {{W-|pap|kama}}
* {{Z|pl}}: {{W-|pl|lózko}}
* {{Z|pt}}: {{W+|pt|cama}} {{m}}, {{W+|pt|leito}}
* {{Z|pa}}: {{W-|pa|ਬਿਸਤਰਾ|R=}}
* {{Z|ro}}: {{W+|ro|pat}}
* {{Z|ru}}: {{W+|ru|кровать|R=krovat’}}
* {{Z|se}}: {{W-|se|seaŋga}}
* {{Z|srn}}: {{W-|srn|bedi}}
* {{Z|so}}: {{W+|so|sariir}}
* {{Z|ses}}: {{W-|ses|daari}}
* {{Z|es}}: {{W+|es|cama}} {{m}}
* {{Z|sw}}: {{W+|sw|kitanda}}
* {{Z|sv}}: {{W+|sv|bädd}}, {{W+|sv|säng}}
* {{Z|tg}}: {{W+|tg|кат|R=kat}}, [[кат|кати]] [[хоб]], {{W+|tg|кроват|R=krovat}}
* {{Z|tl}}: {{W-|tl|káma}}
* {{Z|th}}: {{W-|th|เฅียง|R=}}
* {{Z|tr}}: {{W+|tr|yatak}}, {{W+|tr|tabla|guman=erê}}, {{W+|tr|tahta|guman=erê}}
* {{Z|bo}}: {{W-|bo|ཉལ་ཁིྲ་|R=nyeti}}
* {{Z|tpi}}: {{W-|tpi|bet}}
* {{Z|ur}}: {{W-|ur|بستر|R=}}
* {{Z|cy}}: {{W+|cy|gwely}}
* {{Z|el}}: {{W+|el|κρεβάτι|R=kreváti}}
* {{Z|fsl}}: [https://web.archive.org/web/20201130115303/http://lsf.wiksign.org/wiki/lit lit]
* {{Z|zu}}: {{W+|zu|umbhede}}
{{werger-bin}}
'''2. rûnişteka padişahan''':
{{werger-ser}}
* {{Z|de}}: {{W+|de|Thron}} {{n}}
* {{Z|bg}}: {{W+|bg|престол|tr=prestól}} {{n}}
* {{Z|cs}}: {{W+|cs|trůn}} {{n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|寶座|sc=Hani}}, {{W+|cmn|宝座|tr=bǎozuò}}, {{W+|cmn|王座|tr=wángzuò}}
* {{Z|id}}: {{W+|id|singgasana}}, {{W+|id|tahta}}
* {{Z|ar}}: {{W-|ar|عرش|tr='erş}}, {{W-|ar|كرسي}} (kursî)
* {{Z|hy}}: {{W+|hy|գահ|tr=gah}}
* {{Z|eo}}: {{W+|eo|trono|xs=Esperanto}}
* {{Z|fa}}: {{W+|fa|تخت|tr=taxt}}, {{W+|fa|عرش|tr='arš}}, {{W+|fa|سریر|tr=sarir}}
* {{Z|fi}}: {{W+|fi|valtaistuin}}
* {{Z|fr}}: {{W+|fr|trône}} {{n}}
* {{Z|gd}}: {{W-|gd|rìgh-chathair}} {{n}}
* {{Z|nl}}: {{W+|nl|troon}} {{n}}
* {{Z|he}}: {{W+|he|כס|tr=kés}} {{n}}, {{W-|he|כיסא מלכות|tr=kisé malkhút}} {{n}}
* {{Z|ang}}: {{W-|ang|gifstol}} {{n}}, {{W-|ang|setl}} {{nt}}
* {{Z|en}}: [[throne]] (official seat of a sovereign)
* {{Z|ia}}: {{W-|ia|throno}}
* {{Z|it}}: {{W+|it|trono}} {{n}}
* {{Z|is}}: {{W+|is|hásæti}}
* {{Z|ja}}: {{W+|ja|王座|tr=ōza}}, {{W+|ja|玉座|tr=ぎょくざ, gyokuza|sc=Jpan}}
* {{Z|ca}}: {{W+|ca|tron}} {{n}}
* {{Z|ko}}: {{W-|ko|와좌|tr=wangjwa}}, {{W-|ko|옥좌|tr=okjwa}}
* {{Z|la}}: {{W-|la|thronus}} {{n}}, {{W+|la|sedem}} {{m}}
* {{Z|lb}}: {{W-|lb|Troun}} {{n}}
* {{Z|mk}}: {{W-|mk|престол}} {{n}}
* {{Z|hu}}: {{W+|hu|trón}}, {{W+|hu|trónus}}, {{W+|hu|trónszék}}
* {{Z|egy}}: {{W-|egy|nst|sc=Egyp}}
** <hiero>n:ns-t:g</hiero>
* {{Z|no}}: {{W-|no|trone|c}}
* {{Z|pl}}: {{W+|pl|tron}} {{n}}
* {{Z|pt}}: {{W+|pt|trono}} {{n}}
* {{Z|ru}}: {{W+|ru|трон|tr=tron}} {{n}}, {{W+|ru|престол|tr=prestól}} {{n}}, {{W+|ru|стол|tr=stol}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}, {{W-|sh|престо}} {{n}}, {{W+|sh|tron}} {{n}}, {{W-|sh|трон}} {{n}}
* {{Z|sl}}: {{W+|sl|prestol}} {{n}}
* {{Z|es}}: {{W+|es|trono}} {{n}}
* {{Z|sv}}: {{W+|sv|tron|c}}
* {{Z|tr}}: {{W+|tr|taht}}
* {{Z|grc}}: [[θρόνος]] (thronos)
{{werger-bin}}
'''3. dep ''':
{{werger-ser}}
* {{Z|en}}: a flat {{W+|en|surface}}
{{werger-bin}}
== {{ziman|en}} ==
=== Bilêvkirin ===
:{{ku-tîp|tekst}}
* {{deng|en|en-us-text.ogg|dever=DYA}}
* {{deng|en|LL-Q1860 (eng)-Grendelkhan-text.wav|Deng|dever=Connecticut, DYA}}
* {{deng|en|LL-Q1860 (eng)-Samuel Muldoon (Muldoonsamuel55)-text.wav|Deng|dever=Aurora, DYA}}
=== Navdêr ===
{{navdêr|en}}
# [[tekst]], [[nivîs]]
#* {{bêmînak|en}}
==== Jê ====
* [[context]]
* [[hypertext]]
* [[pretext]]
* [[subtext]]
* [[textual]]
6kp2cnbdr0kmx4kyifw385x4hdy2kvt
6277049
6277046
2026-07-24T02:13:35Z
Ghybu
1393
/* Navdêr */
6277049
wikitext
text/x-wiki
{{bnr-h|Text}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|text|nêr}}
[[Wêne:Vrba postelja.jpg|thumb|text (1)]]
[[Wêne:Polish throne at Warsaw Royal Castle.PNG|thumb|text (2)]]
{{navdêr|ku|z=n}}, {{t-ya nerm}} (1,2)
# [[dar|Darê]] [[nivîn]] li ser tên [[bi cih kirin]] û mirov li ser [[nivistin|dinive]] anku [[razan|radize]] anku [[raketin|radikeve]].
#: {{hevmane|ku|nivîn}}
#: {{mk|ku|Ez li ser '''text''' nanivim, li erdê li ser doşekê baştir xewa min tê.}}
#* {{jêder-pirtûk|ku|jêgirtin=Çil '''text''' di hindurê miẍarê de danî ne . Lawik kete bin '''textekî''' û xwe veşart.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (ÇÎROKA QUNDIRÊ)|cih=Parîs|sal=1940|rûpel=166}}
# [[erş]], [[kursî|kursiya]] [[fermî]] ya [[padişah]]î
#: ''[[paytext]]''
# [[texte]], [[dar]], [[dep]], [[dirext]]
#* {{bêmînak|ku}}
==== Ji wêjeya klasîk ====
* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Dîsa ciwan bû min di rext</br>Ew lami'a dayî ji '''text'''</br>Ma kane berqen xulleba}}
==== Bi alfabeyên din ====
* {{ku-ar|تهخت}}
==== Formên alternatîv ====
* [[taxt]]
==== Jê ====
* [[textî]]
* [[textîn]]
* [[textînî]]
==== Etîmolojî ====
Têkildarî peyva ''[[dirext]]'' anku ''[[dar]]'', [[hevreh]]a {{g|fa|تخت|tr=text}}, pehlewî ''text, taxt'', ji {{ji|ira|ku|-}}.
==== Werger ====
{{werger-ser|nivîn|1}}
* {{Z|af}}: {{W+|af|bed}}
* {{Z|sq}}: {{W+|sq|krevat}} {{n}} ; {{W+|sq|shtrat}}, {{W-|sq|shrati}}
* {{Z|de}}: {{W+|de|Brett}} {{nt}}, {{W+|de|Bohle}} {{m}}, {{W+|de|Bett}} {{nt}}, {{W+|de|Bettstelle}}, {{W+|de|Bettgestell}} {{nt}}, {{W+|de|Falle}} {{m}}, {{W+|de|Holzstück}}, {{W+|de|Podest}}, {{W+|de|Tafel}} {{m}}
* {{Z|bm}}: {{W-|bm|glan}}
* {{Z|eu}}: {{W+|eu|ohe}}
* {{Z|bn}}: {{W+|bn|বিছানা|R=}}
* {{Z|bi}}: {{W-|bi|bed}}
* {{Z|br}}: {{W+|br|gwele}} {{n}}
* {{Z|my}}: {{W-|my|အိပ္ရာ}} (ʔeʔya)
* {{Z|cs}}: {{W+|cs|postel}}
* {{Z|zh}}: {{W+|zh|床|R=chuáng}}
* {{Z|da}}: {{W+|da|seng}}
* {{Z|dog}}: {{W-|dog|damm}}
* {{Z|id}}: {{W-|id|tempat tidur}}, {{W+|id|ranjang}}
* {{Z|ar}}: {{W-|ar|الكرسي|guman=erê}}, {{W+|ar|خشب|guman=erê}},{{W+|ar|سرير}}
* {{Z|hy}}: {{W+|hy|մահճակալ|R=mahč̣akal}}
* {{Z|eo}}: {{W+|eo|lito}}
* {{Z|fa}}: {{W+|fa|بستر|tr=bister}}, {{W-|fa|تخت حکمرانی|guman=erê}}, {{W-|fa|یاتاق|guman=erê}}
* {{Z|fo}}: {{W-|fo|song}}
* {{Z|fi}}: {{W+|fi|vuode}}
* {{Z|fr}}: {{W+|fr|lit|n}}
* {{Z|fy}}: {{W-|fy|bêd}}
* {{Z|gu}}: {{W-|gu|બિછાનું}} ; {{W+|gu|શય્યા}}
* {{Z|hi}}: {{W+|hi|बिस्तर|R=bistar}}
* {{Z|nl}}: {{W+|nl|bed}} {{nt}}, {{W+|nl|legerstede}}, {{W+|nl|sponde}}
* {{Z|io}}: {{W+|io|lito}}
* {{Z|ang}}: {{W+|ang|bed}}, {{W+|ang|bedd}}
* {{Z|en}}: {{W+|en|bed}}, {{W+|en|batten|guman=erê}}, {{W+|en|couch|guman=erê}}, {{W+|en|divan|guman=erê}}, {{W-|en|fashion-plate|guman=erê}}, {{W+|en|hylo-|guman=erê}}, {{W+|en|lair|guman=erê}}, {{W+|en|lign-|guman=erê}}, {{W+|en|settee|guman=erê}}, {{W+|en|tablet|guman=erê}}, {{W+|en|throne|guman=erê}}, {{W+|en|wash basin|guman=erê}}, {{W+|en|washbasin|guman=erê}}, {{W+|en|washbowl|guman=erê}}, {{W+|en|wood|guman=erê}}
* {{Z|iu}}: {{W+|iu|ᐃᒡᓕᖅ|R=[[igliq]]}}
* {{Z|it}}: {{W+|it|letto}} {{n}}
* {{Z|is}}: {{W-|is|beð}} {{nt}} ; {{W+|is|rúm}}
* {{Z|ja}}: {{W-|ja|ベッド, beddo}}
* {{Z|ca}}: {{W+|ca|llit}}
* {{Z|ko}}: {{W+|ko|침대|R=chimdae}}
* {{Z|kw}}: {{W-|kw|gwely}}
* {{Z|lv}}: {{W-|lv|gulta}}
* {{Z|lt}}: {{W+|lt|lova}}; {{W-|lt|guolis}}
* {{Z|ms}}: {{W+|ms|katil}}
* {{Z|mr}}: {{W-|mr|बिछाना|R=}}
* {{Z|hu}}: {{W+|hu|ágy}}
* {{Z|no}}: {{W+|no|seng}} {{n}}
* {{Z|pap}}: {{W-|pap|kama}}
* {{Z|pl}}: {{W-|pl|lózko}}
* {{Z|pt}}: {{W+|pt|cama}} {{m}}, {{W+|pt|leito}}
* {{Z|pa}}: {{W-|pa|ਬਿਸਤਰਾ|R=}}
* {{Z|ro}}: {{W+|ro|pat}}
* {{Z|ru}}: {{W+|ru|кровать|R=krovat’}}
* {{Z|se}}: {{W-|se|seaŋga}}
* {{Z|srn}}: {{W-|srn|bedi}}
* {{Z|so}}: {{W+|so|sariir}}
* {{Z|ses}}: {{W-|ses|daari}}
* {{Z|es}}: {{W+|es|cama}} {{m}}
* {{Z|sw}}: {{W+|sw|kitanda}}
* {{Z|sv}}: {{W+|sv|bädd}}, {{W+|sv|säng}}
* {{Z|tg}}: {{W+|tg|кат|R=kat}}, [[кат|кати]] [[хоб]], {{W+|tg|кроват|R=krovat}}
* {{Z|tl}}: {{W-|tl|káma}}
* {{Z|th}}: {{W-|th|เฅียง|R=}}
* {{Z|tr}}: {{W+|tr|yatak}}, {{W+|tr|tabla|guman=erê}}, {{W+|tr|tahta|guman=erê}}
* {{Z|bo}}: {{W-|bo|ཉལ་ཁིྲ་|R=nyeti}}
* {{Z|tpi}}: {{W-|tpi|bet}}
* {{Z|ur}}: {{W-|ur|بستر|R=}}
* {{Z|cy}}: {{W+|cy|gwely}}
* {{Z|el}}: {{W+|el|κρεβάτι|R=kreváti}}
* {{Z|fsl}}: [https://web.archive.org/web/20201130115303/http://lsf.wiksign.org/wiki/lit lit]
* {{Z|zu}}: {{W+|zu|umbhede}}
{{werger-bin}}
{{werger-ser|rûnişteka padişahan|2}}
* {{Z|de}}: {{W+|de|Thron}} {{n}}
* {{Z|bg}}: {{W+|bg|престол|tr=prestól}} {{n}}
* {{Z|cs}}: {{W+|cs|trůn}} {{n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|寶座|sc=Hani}}, {{W+|cmn|宝座|tr=bǎozuò}}, {{W+|cmn|王座|tr=wángzuò}}
* {{Z|id}}: {{W+|id|singgasana}}, {{W+|id|tahta}}
* {{Z|ar}}: {{W-|ar|عرش|tr='erş}}, {{W-|ar|كرسي}} (kursî)
* {{Z|hy}}: {{W+|hy|գահ|tr=gah}}
* {{Z|eo}}: {{W+|eo|trono|xs=Esperanto}}
* {{Z|fa}}: {{W+|fa|تخت|tr=taxt}}, {{W+|fa|عرش|tr='arš}}, {{W+|fa|سریر|tr=sarir}}
* {{Z|fi}}: {{W+|fi|valtaistuin}}
* {{Z|fr}}: {{W+|fr|trône}} {{n}}
* {{Z|gd}}: {{W-|gd|rìgh-chathair}} {{n}}
* {{Z|nl}}: {{W+|nl|troon}} {{n}}
* {{Z|he}}: {{W+|he|כס|tr=kés}} {{n}}, {{W-|he|כיסא מלכות|tr=kisé malkhút}} {{n}}
* {{Z|ang}}: {{W-|ang|gifstol}} {{n}}, {{W-|ang|setl}} {{nt}}
* {{Z|en}}: [[throne]] (official seat of a sovereign)
* {{Z|ia}}: {{W-|ia|throno}}
* {{Z|it}}: {{W+|it|trono}} {{n}}
* {{Z|is}}: {{W+|is|hásæti}}
* {{Z|ja}}: {{W+|ja|王座|tr=ōza}}, {{W+|ja|玉座|tr=ぎょくざ, gyokuza|sc=Jpan}}
* {{Z|ca}}: {{W+|ca|tron}} {{n}}
* {{Z|ko}}: {{W-|ko|와좌|tr=wangjwa}}, {{W-|ko|옥좌|tr=okjwa}}
* {{Z|la}}: {{W-|la|thronus}} {{n}}, {{W+|la|sedem}} {{m}}
* {{Z|lb}}: {{W-|lb|Troun}} {{n}}
* {{Z|mk}}: {{W-|mk|престол}} {{n}}
* {{Z|hu}}: {{W+|hu|trón}}, {{W+|hu|trónus}}, {{W+|hu|trónszék}}
* {{Z|egy}}: {{W-|egy|nst|sc=Egyp}}
** <hiero>n:ns-t:g</hiero>
* {{Z|no}}: {{W-|no|trone|c}}
* {{Z|pl}}: {{W+|pl|tron}} {{n}}
* {{Z|pt}}: {{W+|pt|trono}} {{n}}
* {{Z|ru}}: {{W+|ru|трон|tr=tron}} {{n}}, {{W+|ru|престол|tr=prestól}} {{n}}, {{W+|ru|стол|tr=stol}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}, {{W-|sh|престо}} {{n}}, {{W+|sh|tron}} {{n}}, {{W-|sh|трон}} {{n}}
* {{Z|sl}}: {{W+|sl|prestol}} {{n}}
* {{Z|es}}: {{W+|es|trono}} {{n}}
* {{Z|sv}}: {{W+|sv|tron|c}}
* {{Z|tr}}: {{W+|tr|taht}}
* {{Z|grc}}: [[θρόνος]] (thronos)
{{werger-bin}}
{{werger-ser|dep|3}}
* {{Z|en}}: a flat {{W+|en|surface}}
{{werger-bin}}
== {{ziman|en}} ==
=== Bilêvkirin ===
:{{ku-tîp|tekst}}
* {{deng|en|en-us-text.ogg|dever=DYA}}
* {{deng|en|LL-Q1860 (eng)-Grendelkhan-text.wav|Deng|dever=Connecticut, DYA}}
* {{deng|en|LL-Q1860 (eng)-Samuel Muldoon (Muldoonsamuel55)-text.wav|Deng|dever=Aurora, DYA}}
=== Navdêr ===
{{navdêr|en}}
# [[tekst]], [[nivîs]]
#* {{bêmînak|en}}
==== Jê ====
* [[context]]
* [[hypertext]]
* [[pretext]]
* [[subtext]]
* [[textual]]
kdjq3rvh920v15dnruff003jeafl76o
6277057
6277049
2026-07-24T02:36:12Z
Ghybu
1393
/* Navdêr */
6277057
wikitext
text/x-wiki
{{bnr-h|Text}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|text|nêr}}
[[Wêne:Vrba postelja.jpg|thumb|text (1)]]
[[Wêne:Polish throne at Warsaw Royal Castle.PNG|thumb|text (2)]]
{{navdêr|ku|z=n}}, {{t-ya nerm}} (1,2)
# [[dar|Darê]] [[nivîn]] li ser tên [[bi cih kirin]] û mirov li ser [[nivistin|dinive]] anku [[razan|radize]] anku [[raketin|radikeve]].
#: {{hevmane|ku|nivîn}}
#: {{mk|ku|Ez li ser '''text''' nanivim, li erdê li ser doşekê baştir xewa min tê.}}
#* {{jêder-pirtûk|ku|jêgirtin=Çil '''text''' di hindurê miẍarê de danî ne . Lawik kete bin '''textekî''' û xwe veşart.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (ÇÎROKA QUNDIRÊ)|cih=Parîs|sal=1940|rûpel=10}}
# [[erş]], [[kursî|kursiya]] [[fermî]] ya [[padişah]]î
#: ''[[paytext]]''
# [[texte]], [[dar]], [[dep]], [[dirext]]
#* {{bêmînak|ku}}
==== Ji wêjeya klasîk ====
* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Dîsa ciwan bû min di rext</br>Ew lami'a dayî ji '''text'''</br>Ma kane berqen xulleba}}
==== Bi alfabeyên din ====
* {{ku-ar|تهخت}}
==== Formên alternatîv ====
* [[taxt]]
==== Jê ====
* [[textî]]
* [[textîn]]
* [[textînî]]
==== Etîmolojî ====
Têkildarî peyva ''[[dirext]]'' anku ''[[dar]]'', [[hevreh]]a {{g|fa|تخت|tr=text}}, pehlewî ''text, taxt'', ji {{ji|ira|ku|-}}.
==== Werger ====
{{werger-ser|nivîn|1}}
* {{Z|af}}: {{W+|af|bed}}
* {{Z|sq}}: {{W+|sq|krevat}} {{n}} ; {{W+|sq|shtrat}}, {{W-|sq|shrati}}
* {{Z|de}}: {{W+|de|Brett}} {{nt}}, {{W+|de|Bohle}} {{m}}, {{W+|de|Bett}} {{nt}}, {{W+|de|Bettstelle}}, {{W+|de|Bettgestell}} {{nt}}, {{W+|de|Falle}} {{m}}, {{W+|de|Holzstück}}, {{W+|de|Podest}}, {{W+|de|Tafel}} {{m}}
* {{Z|bm}}: {{W-|bm|glan}}
* {{Z|eu}}: {{W+|eu|ohe}}
* {{Z|bn}}: {{W+|bn|বিছানা|R=}}
* {{Z|bi}}: {{W-|bi|bed}}
* {{Z|br}}: {{W+|br|gwele}} {{n}}
* {{Z|my}}: {{W-|my|အိပ္ရာ}} (ʔeʔya)
* {{Z|cs}}: {{W+|cs|postel}}
* {{Z|zh}}: {{W+|zh|床|R=chuáng}}
* {{Z|da}}: {{W+|da|seng}}
* {{Z|dog}}: {{W-|dog|damm}}
* {{Z|id}}: {{W-|id|tempat tidur}}, {{W+|id|ranjang}}
* {{Z|ar}}: {{W-|ar|الكرسي|guman=erê}}, {{W+|ar|خشب|guman=erê}},{{W+|ar|سرير}}
* {{Z|hy}}: {{W+|hy|մահճակալ|R=mahč̣akal}}
* {{Z|eo}}: {{W+|eo|lito}}
* {{Z|fa}}: {{W+|fa|بستر|tr=bister}}, {{W-|fa|تخت حکمرانی|guman=erê}}, {{W-|fa|یاتاق|guman=erê}}
* {{Z|fo}}: {{W-|fo|song}}
* {{Z|fi}}: {{W+|fi|vuode}}
* {{Z|fr}}: {{W+|fr|lit|n}}
* {{Z|fy}}: {{W-|fy|bêd}}
* {{Z|gu}}: {{W-|gu|બિછાનું}} ; {{W+|gu|શય્યા}}
* {{Z|hi}}: {{W+|hi|बिस्तर|R=bistar}}
* {{Z|nl}}: {{W+|nl|bed}} {{nt}}, {{W+|nl|legerstede}}, {{W+|nl|sponde}}
* {{Z|io}}: {{W+|io|lito}}
* {{Z|ang}}: {{W+|ang|bed}}, {{W+|ang|bedd}}
* {{Z|en}}: {{W+|en|bed}}, {{W+|en|batten|guman=erê}}, {{W+|en|couch|guman=erê}}, {{W+|en|divan|guman=erê}}, {{W-|en|fashion-plate|guman=erê}}, {{W+|en|hylo-|guman=erê}}, {{W+|en|lair|guman=erê}}, {{W+|en|lign-|guman=erê}}, {{W+|en|settee|guman=erê}}, {{W+|en|tablet|guman=erê}}, {{W+|en|throne|guman=erê}}, {{W+|en|wash basin|guman=erê}}, {{W+|en|washbasin|guman=erê}}, {{W+|en|washbowl|guman=erê}}, {{W+|en|wood|guman=erê}}
* {{Z|iu}}: {{W+|iu|ᐃᒡᓕᖅ|R=[[igliq]]}}
* {{Z|it}}: {{W+|it|letto}} {{n}}
* {{Z|is}}: {{W-|is|beð}} {{nt}} ; {{W+|is|rúm}}
* {{Z|ja}}: {{W-|ja|ベッド, beddo}}
* {{Z|ca}}: {{W+|ca|llit}}
* {{Z|ko}}: {{W+|ko|침대|R=chimdae}}
* {{Z|kw}}: {{W-|kw|gwely}}
* {{Z|lv}}: {{W-|lv|gulta}}
* {{Z|lt}}: {{W+|lt|lova}}; {{W-|lt|guolis}}
* {{Z|ms}}: {{W+|ms|katil}}
* {{Z|mr}}: {{W-|mr|बिछाना|R=}}
* {{Z|hu}}: {{W+|hu|ágy}}
* {{Z|no}}: {{W+|no|seng}} {{n}}
* {{Z|pap}}: {{W-|pap|kama}}
* {{Z|pl}}: {{W-|pl|lózko}}
* {{Z|pt}}: {{W+|pt|cama}} {{m}}, {{W+|pt|leito}}
* {{Z|pa}}: {{W-|pa|ਬਿਸਤਰਾ|R=}}
* {{Z|ro}}: {{W+|ro|pat}}
* {{Z|ru}}: {{W+|ru|кровать|R=krovat’}}
* {{Z|se}}: {{W-|se|seaŋga}}
* {{Z|srn}}: {{W-|srn|bedi}}
* {{Z|so}}: {{W+|so|sariir}}
* {{Z|ses}}: {{W-|ses|daari}}
* {{Z|es}}: {{W+|es|cama}} {{m}}
* {{Z|sw}}: {{W+|sw|kitanda}}
* {{Z|sv}}: {{W+|sv|bädd}}, {{W+|sv|säng}}
* {{Z|tg}}: {{W+|tg|кат|R=kat}}, [[кат|кати]] [[хоб]], {{W+|tg|кроват|R=krovat}}
* {{Z|tl}}: {{W-|tl|káma}}
* {{Z|th}}: {{W-|th|เฅียง|R=}}
* {{Z|tr}}: {{W+|tr|yatak}}, {{W+|tr|tabla|guman=erê}}, {{W+|tr|tahta|guman=erê}}
* {{Z|bo}}: {{W-|bo|ཉལ་ཁིྲ་|R=nyeti}}
* {{Z|tpi}}: {{W-|tpi|bet}}
* {{Z|ur}}: {{W-|ur|بستر|R=}}
* {{Z|cy}}: {{W+|cy|gwely}}
* {{Z|el}}: {{W+|el|κρεβάτι|R=kreváti}}
* {{Z|fsl}}: [https://web.archive.org/web/20201130115303/http://lsf.wiksign.org/wiki/lit lit]
* {{Z|zu}}: {{W+|zu|umbhede}}
{{werger-bin}}
{{werger-ser|rûnişteka padişahan|2}}
* {{Z|de}}: {{W+|de|Thron}} {{n}}
* {{Z|bg}}: {{W+|bg|престол|tr=prestól}} {{n}}
* {{Z|cs}}: {{W+|cs|trůn}} {{n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|寶座|sc=Hani}}, {{W+|cmn|宝座|tr=bǎozuò}}, {{W+|cmn|王座|tr=wángzuò}}
* {{Z|id}}: {{W+|id|singgasana}}, {{W+|id|tahta}}
* {{Z|ar}}: {{W-|ar|عرش|tr='erş}}, {{W-|ar|كرسي}} (kursî)
* {{Z|hy}}: {{W+|hy|գահ|tr=gah}}
* {{Z|eo}}: {{W+|eo|trono|xs=Esperanto}}
* {{Z|fa}}: {{W+|fa|تخت|tr=taxt}}, {{W+|fa|عرش|tr='arš}}, {{W+|fa|سریر|tr=sarir}}
* {{Z|fi}}: {{W+|fi|valtaistuin}}
* {{Z|fr}}: {{W+|fr|trône}} {{n}}
* {{Z|gd}}: {{W-|gd|rìgh-chathair}} {{n}}
* {{Z|nl}}: {{W+|nl|troon}} {{n}}
* {{Z|he}}: {{W+|he|כס|tr=kés}} {{n}}, {{W-|he|כיסא מלכות|tr=kisé malkhút}} {{n}}
* {{Z|ang}}: {{W-|ang|gifstol}} {{n}}, {{W-|ang|setl}} {{nt}}
* {{Z|en}}: [[throne]] (official seat of a sovereign)
* {{Z|ia}}: {{W-|ia|throno}}
* {{Z|it}}: {{W+|it|trono}} {{n}}
* {{Z|is}}: {{W+|is|hásæti}}
* {{Z|ja}}: {{W+|ja|王座|tr=ōza}}, {{W+|ja|玉座|tr=ぎょくざ, gyokuza|sc=Jpan}}
* {{Z|ca}}: {{W+|ca|tron}} {{n}}
* {{Z|ko}}: {{W-|ko|와좌|tr=wangjwa}}, {{W-|ko|옥좌|tr=okjwa}}
* {{Z|la}}: {{W-|la|thronus}} {{n}}, {{W+|la|sedem}} {{m}}
* {{Z|lb}}: {{W-|lb|Troun}} {{n}}
* {{Z|mk}}: {{W-|mk|престол}} {{n}}
* {{Z|hu}}: {{W+|hu|trón}}, {{W+|hu|trónus}}, {{W+|hu|trónszék}}
* {{Z|egy}}: {{W-|egy|nst|sc=Egyp}}
** <hiero>n:ns-t:g</hiero>
* {{Z|no}}: {{W-|no|trone|c}}
* {{Z|pl}}: {{W+|pl|tron}} {{n}}
* {{Z|pt}}: {{W+|pt|trono}} {{n}}
* {{Z|ru}}: {{W+|ru|трон|tr=tron}} {{n}}, {{W+|ru|престол|tr=prestól}} {{n}}, {{W+|ru|стол|tr=stol}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}
* {{Z|sh}}: {{W+|sh|presto}} {{n}}, {{W-|sh|престо}} {{n}}, {{W+|sh|tron}} {{n}}, {{W-|sh|трон}} {{n}}
* {{Z|sl}}: {{W+|sl|prestol}} {{n}}
* {{Z|es}}: {{W+|es|trono}} {{n}}
* {{Z|sv}}: {{W+|sv|tron|c}}
* {{Z|tr}}: {{W+|tr|taht}}
* {{Z|grc}}: [[θρόνος]] (thronos)
{{werger-bin}}
{{werger-ser|dep|3}}
* {{Z|en}}: a flat {{W+|en|surface}}
{{werger-bin}}
== {{ziman|en}} ==
=== Bilêvkirin ===
:{{ku-tîp|tekst}}
* {{deng|en|en-us-text.ogg|dever=DYA}}
* {{deng|en|LL-Q1860 (eng)-Grendelkhan-text.wav|Deng|dever=Connecticut, DYA}}
* {{deng|en|LL-Q1860 (eng)-Samuel Muldoon (Muldoonsamuel55)-text.wav|Deng|dever=Aurora, DYA}}
=== Navdêr ===
{{navdêr|en}}
# [[tekst]], [[nivîs]]
#* {{bêmînak|en}}
==== Jê ====
* [[context]]
* [[hypertext]]
* [[pretext]]
* [[subtext]]
* [[textual]]
2uya4uiuharoihgbj37wk4iu26sv534
tîr
0
15656
6277087
6230492
2026-07-24T11:29:14Z
IbrahimKocher Duhok
4280
Werger: +îngilîzî : [[strip fields]] (bi alîkarî)
6277087
wikitext
text/x-wiki
{{bnr-h|TIR|Tîr|tîr|tir|-tir}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{beş|Navdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr (navdêr).wav|Deng|Bidlîs}}
* {{beş|Rengdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr 1 ===
{{ku-tewîn-nav|tîr|mê}}
[[Wêne:WA archery target with arrows.jpg|thumb|150px|tîr]]
{{navdêr|ku|z=m}}
# {{f|ku|çek}} [[çek|Çek]]ek [[serik]][[tûj]] e ku ji [[kevan]]ê tê [[avêtin]].
#* {{jêder|ku|jêgirtin=Eger ku awir '''tîr''' bin, çav û birû jî kevan in. Di helbestan de wekî evîndar birûyên xwe xwar bike ev mîna kevanê vedikişe, tê pejirandin. Birû ji ber ku '''tîra''' herî dijwar biaveje xwe vedikişîne.|''[[w:Di Helbestên Melayê Bateyî de Evîn û Hezkirin|Di Helbestên Melayê Bateyî de Evîn û Hezkirin]]'', [[w:Reşît Rojdar Yildiz|Reşît Rojdar Yildiz]], Bingöl Üniversitesi Yaşayan Diller Enstitüsü Dergisi}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Ji wê rojê dizanî min</br>Ko yar ew '''tîr''' li cergê da</br>Gelo dîsa bibînim ez}}
#: ''Reş e weke qîrê,</br>Rast e weke '''tîrê'''.''
==== Bi alfabeyên din ====
* {{ku-ar|تیر}}
==== Bide ber ====
* {{g|ku|rim}}
* {{g|ku|peygan}}
==== Jê ====
{{kol3|ku|cure=Jê
|sertîrik
|sertîrikî
|tîravêj
|tîravêjî
|tîrik
|tîrk
}}
==== Etîmolojî ====
Hevreha soranî {{g|ckb|تیر|tr=tîr}}, farisî {{g|fa|تیر|tr=tîr}}, tacikî [[тир]] (tîr), pehlewî ''tigr'', avestayî ''tiẍre-, tiẍri-'', ji {{ji|ira}}. Herwiha bide ber sûmerî [[𒋾]] (ti: tîr)
==== Binêre herwiha ====
{{lîste:çek/ku}}
==== Bi zaravayên din ====
* Soranî: {{W-|ckb|تیر|tr=tîr}}
==== Werger ====
{{werger-ser|tîr wek çek yan nîşan}}
* {{Z|abq}}: {{W-|abq|сагӏындакъ}}
* {{Z|ab}}: {{W-|ab|ахәымпал|sc=Cyrl}}
* {{Z|ady}}: {{W-|ady|щэбзащ}}
* {{Z|af}}: {{W-|af|pyl}}, {{W-|af|skig}}
* {{Z|ak}}: {{W-|ak|agyan}}
* {{Z|sq}}: {{W+|sq|shigjetë|m}}, {{W+|sq|ber|n}}
* {{Z|de}}: {{W+|de|Pfeil|n}}
* {{Z|amn}}: {{W-|amn|fak}}
* {{Z|am}}: {{W-|am|ፍላጣ|sc=Ethi}}, {{W-|am|ቀስት|sc=Ethi}}, {{W-|am|ፍላጻ|sc=Ethi}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|k’aa’ist'aaní}}
* {{Z|arc}}:
*: {{Z|syc}}: {{W-|syc|ܓܐܪܐ|tr=giʾrā|sc=Syrc}}
* {{Z|arw}}: {{W-|arw|simala}}
* {{Z|rup}}: {{W-|rup|sãdzeatã}}
* {{Z|as}}: {{W-|as|কাঁড়}}
* {{Z|ast}}: {{W-|ast|flecha|m}}
* {{Z|av}}: {{W-|av|йойохул гӏучӏ|sc=Cyrl}}, {{W-|av|чӏор|sc=Cyrl}}
* {{Z|ae}}: {{W-|ae|𐬙𐬌𐬔𐬭𐬌}}
* {{Z|ay}}: {{W-|ay|mich'i}}
* {{Z|az}}: {{W+|az|ox}}
*: Kirîlî: {{W-|az|ох|sc=Cyrl}}
*: {{Z|az}}: {{W-|az|اوخ|tr=ox|sc=fa-Arab}}
* {{Z|eu}}: {{W+|eu|gezi}}
* {{Z|ba}}: {{W-|ba|уҡ|sc=Cyrl}}
* {{Z|be}}: {{W-|be|страла́|m|sc=Cyrl}}
* {{Z|bn}}: {{W+|bn|তীর|sc=Beng}}
* {{Z|bi}}: {{W-|bi|ara}}
* {{Z|bor}}: {{W-|bor|tugo}}
* {{Z|bg}}: {{W+|bg|стрела́|m|sc=Cyrl}}
* {{Z|my}}: {{W+|my|မြား|sc=Mymr}}
* {{Z|ccc}}: {{W-|ccc|ashkololi}}
* {{Z|ce}}: {{W-|ce|пха}}
* {{Z|cs}}: {{W+|cs|šíp|n}}, {{W+|cs|střela|m}}
* {{Z|chr}}: {{W-|chr|ᎫᏂ}}
* {{Z|chy}}: {{W-|chy|maahe}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|箭|tr=zin3}}, {{W-|yue|矢|tr=ci2}}
*: {{Z|dng}}: {{W-|dng|җян}}
*: {{Z|cmn}}: {{W+|cmn|箭|tr=jiàn}}, {{W+|cmn|矢|tr=shǐ}}, {{W+|cmn|箭頭|tr=jiàntóu}}
*: {{Z|nan}}: {{W-|nan|箭|tr=chìⁿ}}, {{W-|nan|矢|tr=sí}}
*: {{Z|wuu}}: {{W-|wuu|箭}}, {{W-|wuu|矢}}
* {{Z|chp}}: {{W-|chp|k’á}}
* {{Z|cho}}: {{W-|cho|oo-ske-noc-kay}}
* {{Z|cv}}: {{W-|cv|ҫӗмрен|sc=Cyrl}}
* {{Z|da}}: {{W-|da|pil|g}}
* {{Z|dar}}: {{W-|dar|урги}}
* {{Z|dv}}: {{W-|dv|ތީރު|sc=Thaa}}
* {{Z|sce}}: {{W-|sce|sumu}}
* {{Z|ovd}}: {{W-|ovd|kuov|n}}
* {{Z|id}}: {{W+|id|panah}}
* {{Z|ar}}: {{W+|ar|سَهْم|n}}
*: {{Z|arz}}: {{W-|arz|سهم|n|tr=sahm}}
*: {{Z|acw}}: {{W-|acw|سَهَم|n|tr=saham}}
* {{Z|hy}}: {{W+|hy|նետ}}
* {{Z|myv}}: {{W-|myv|нал}}, {{W-|myv|нал}}
* {{Z|eo}}: {{W+|eo|sago}}
* {{Z|et}}: {{W+|et|nool}}
* {{Z|evn}}: {{W-|evn|нюр}}
* {{Z|ee}}: {{W-|ee|aŋutrɔ|nt}}
* {{Z|pal}}: {{W-|pal|𐭲𐭢𐭫|ts=tigr}}
* {{Z|fa}}: {{W+|fa|تیر|tr=tir}}
* {{Z|fo}}: {{W-|fo|ørvur|n}}, {{W-|fo|pílur|n}}, {{W-|fo|ørv|m}}
* {{Z|fi}}: {{W+|fi|nuoli}}
* {{Z|fr}}: {{W+|fr|flèche|m}}
* {{Z|fur}}: {{W-|fur|frece|m}}
* {{Z|stq}}: {{W-|stq|Stroal|n}}
* {{Z|fy}}: {{W-|fy|pylk|nt}}
* {{Z|gd}}: {{W-|gd|saighead|m}}, {{W-|gd|gath|m}}
* {{Z|gl}}: {{W-|gl|frecha|m}}, {{W-|gl|frisga|m}}, {{W+|gl|seta|m}}
* {{Z|got}}: {{W-|got|𐌰𐍂𐍈𐌰𐌶𐌽𐌰|m}}
* {{Z|gu}}: {{W-|gu|બાણ|sc=Gujr}}
* {{Z|ka}}: {{W-|ka|ისარი|sc=Geor}}
* {{Z|haw}}: {{W-|haw|pua}}, {{W-|haw|pua pana}}
* {{Z|ha}}: {{W-|ha|kibiya}}
* {{Z|hi}}: {{W+|hi|तीर|n}}, {{W+|hi|बाण|sc=Deva}}
* {{Z|nl}}: {{W+|nl|pijl|n}}
* {{Z|hrx}}: {{W-|hrx|Peil|n}}
* {{Z|he}}: {{W+|he|חֵץ|n|tr=khets|sc=Hebr}}
* {{Z|io}}: {{W+|io|flecho}}
* {{Z|ig}}: {{W-|ig|arọ}}
* {{Z|ang}}: {{W-|ang|flān|n}}
* {{Z|en}}: {{W+|en|arrow}}
* {{Z|inh}}: {{W-|inh|тӏаьск}}
* {{Z|ia}}: {{W-|ia|flecha}}, {{W-|ia|sagitta}}
* {{Z|ga}}: {{W-|ga|saighead|m}}
* {{Z|zai}}: {{W-|zai|baxa}}, {{W-|zai|baza}}
* {{Z|it}}: {{W+|it|freccia|m}}
* {{Z|is}}: {{W+|is|ör|m}}
* {{Z|ja}}: {{W+|ja|矢|tr=や, ya}}
* {{Z|jra}}: {{W-|jra|ƀrom}}
* {{Z|kaw}}: {{W-|kaw|panah}}
* {{Z|jv}}: {{W+|jv|ꦥꦤꦃ}}
* {{Z|kbd}}: {{W-|kbd|шабзэ}}
* {{Z|kl}}: {{W-|kl|qarsoq}}
* {{Z|kn}}: {{W+|kn|ಅಂಬು|sc=Knda}}, {{W+|kn|ಬಾಣ|sc=Knda}}
* {{Z|krc}}: {{W-|krc|окъ|sc=Cyrl}}
* {{Z|krl}}: {{W-|krl|piili}}
* {{Z|ca}}: {{W+|ca|fletxa|m}}, {{W+|ca|sageta|m}}
* {{Z|qu}}: {{W-|qu|wach'i}}
* {{Z|ket}}: {{W-|ket|ӄам}}
* {{Z|ky}}: {{W+|ky|жебе|sc=Cyrl}}
* {{Z|ki}}: {{W-|ki|mũguĩ|c3}}
* {{Z|si}}: {{W-|si|ඊය|sc=Sinh}}
* {{Z|koi}}: {{W-|koi|ньӧв}}
* {{Z|ko}}: {{W+|ko|화살}}
* {{Z|kw}}: {{W-|kw|seth}}
* {{Z|kum}}: {{W-|kum|окъ}}
* {{Z|lbe}}: {{W-|lbe|чӏатӏаракӏ}}
* {{Z|lkt}}: {{W-|lkt|waŋhíŋkpe}}
* {{Z|ltg}}: {{W-|ltg|šautra|m}}
* {{Z|la}}: {{W+|la|sagitta|m}}
* {{Z|lv}}: {{W-|lv|bulta|m}}, {{W-|lv|šautra|m}}, {{W-|lv|strēle|m}}
* {{Z|lo}}: {{W-|lo|ລູກສອນ}}, {{W-|lo|ສອນ}}
* {{Z|ln}}: {{W-|ln|likulá}}
* {{Z|lt}}: {{W-|lt|strėlė|m}}
* {{Z|khb}}: {{W-|khb|ᦔᦹᧃ|sc=Talu}}, {{W-|khb|ᦐᦱᧉᦺᦙᧉ}}
* {{Z|lb}}: {{W-|lb|Fäil|n}}
* {{Z|mk}}: {{W-|mk|стрела|m|sc=Cyrl}}
* {{Z|ml}}: {{W+|ml|അമ്പ്|sc=Mlym}}
* {{Z|ms}}: {{W+|ms|panah}}
*: {{Z|ms}}: {{W-|ms|ڤانه|sc=Arab}}
* {{Z|mt}}: {{W-|mt|vleġġa|m}}
* {{Z|mnc}}: {{W-|mnc|ᠰᡳᡵᡩᠠᠨ}}, {{W-|mnc|ᠨᡳᡵᡠ}}
* {{Z|mns-nor}}: {{W-|mns-nor|ня̄л}}
* {{Z|mi}}: {{W-|mi|kōpere}}, {{W-|mi|pere}}
* {{Z|mr}}: {{W-|mr|बाण}}
* Marî:
*: {{Z|mhr}}: {{W-|mhr|пикш}}
* {{Z|hu}}: {{W+|hu|nyíl}}
* {{Z|egy}}: {{W-egy|šsr|n|h=V6-s-r:T11}}, {{W-egy|zwn|n|h=z-wn:n-T11}}
* {{Z|moh}}: {{W-|moh|kayonkwere}}
* {{Z|mdf}}: {{W-|mdf|нал}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|сум}}
*: Mongolî: {{W-|mn|ᠰᠤᠮᠤ}}
* {{Z|mnw}}: {{W-|mnw|လဴ}}
* {{Z|nci}}: {{W-|nci|mitl}}
* {{Z|nah}}: {{W+|nah|mitl}}
* {{Z|nnt}}: {{W-|nnt|allontz}}
* {{Z|nv}}: {{W-|nv|kʼaaʼ}}
* {{Z|nds}}: {{W-|nds|Piel|n}}, {{W-|nds|Piil|n}}
* {{Z|non}}: {{W-|non|ǫr|m}}
* {{Z|no}}: {{W+|no|pil}}
*: {{Z|nb}}: {{W+|nb|pil|n|m}}
*: {{Z|nn}}: {{W-|nn|pil|m}}
* {{Z|nwy}}: {{W-|nwy|aruntquaserank}}
* {{Z|oj}}: {{W-|oj|mitigwanwi}}, {{W-|oj|bikwak}}, {{W-|oj|ginwaakwanwi}}, {{W-|oj|gii'anwaakonwi}}, {{W-|oj|asawaan}}
* {{Z|oc}}: {{W+|oc|flècha|m}}, {{W+|oc|sageta|m}}
* {{Z|one}}: {{W-|one|kayú:kwile̲’}}
* {{Z|or}}: {{W+|or|ତୀର}}
* {{Z|os}}: {{W-|os|фат|sc=Cyrl}}
* {{Z|ota}}: {{W-|ota|اوق|tr=ok}}, {{W-|ota|تیر|tr=tir}}, {{W-|ota|سهم|tr=sehm}}
* {{Z|ug}}: {{W-|ug|ئوق|sc=ug-Arab}}
* {{Z|uz}}: {{W+|uz|oʻq}}, {{W+|uz|tir}}
* {{Z|pi}}: {{W-|pi|usu}}, {{W-|pi|sara|n}}, {{W-|pi|bāṇa|n}}
* {{Z|ps}}: {{W-|ps|غشۍ|tr=ǧašëy|sc=ps-Arab}}
* {{Z|myp}}: {{W-|myp|kahaí}}
* {{Z|pdt}}: {{W-|pdt|Feil|m}}
* {{Z|pl}}: {{W+|pl|strzała|m}}, {{W-|pl|szyp|n}}
* {{Z|roa-opt}}: {{W-|roa-opt|saeta}}
* {{Z|pt}}: {{W+|pt|flecha|m}}, {{W+|pt|seta|m}}
* {{Z|pa}}: {{W-|pa|ਤੀਰ}}
* {{Z|kk}}: {{W-|kk|жебе|sc=Cyrl}}, {{W-|kk|оқ|sc=Cyrl}}
* {{Z|mwr}}: {{W-|mwr|तीर|tr=tīr}}
* {{Z|rhg}}: {{W-|rhg|tir}}
* {{Z|rm}}: {{W-|rm|frizza|m}}
* {{Z|ro}}: {{W+|ro|săgeată|m}}
*: Kirîlî: {{W-|ro|сэӂатэ|m}}
* {{Z|ru}}: {{W+|ru|стрела́|m}}
* {{Z|se}}: {{W-|se|njuolla}}
* {{Z|sa}}: {{W+|sa|इषु|n}}, {{W+|sa|बाण}}, {{W+|sa|शर|n}}
* {{Z|scn}}: {{W-|scn|freccia|m}}
* {{Z|sd}}: {{W+|sd|پيلو|tr=pīlō|cuda=پيِلو|sc=sd-Arab}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|стрела|m}}, {{W-|sh|стријела|m}}, {{W-|sh|стрелица|m}}, {{W-|sh|стрјелица|m}}, {{W-|sh|стрила|m}}
*: Latînî: {{W-|sh|strela|m}}, {{W+|sh|strijela|m}}, {{W+|sh|strelica|m}}, {{W-|sh|strjelica|m}}, {{W-|sh|strila|m}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|стрѣла|m|sc=Cyrs}}
*: Glagolîtîk: {{W-|cu|ⱄⱅⱃⱑⰾⰰ|m|sc=Glag}}
* {{Z|orv}}: {{W-|orv|стрѣла|m}}
* {{Z|sk}}: {{W-|sk|šíp}}, {{W-|sk|strela|m}}
* {{Z|sl}}: {{W+|sl|puščica|m}}
* {{Z|zlw-slv}}: {{W-|zlw-slv|střåula|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|klek|n}}, {{W-|dsb|šypa|m}}
*: {{Z|hsb}}: {{W-|hsb|kłok|n}}, {{W-|hsb|šip|n}}
* {{Z|st}}: {{W-|st|motsu}}
* {{Z|es}}: {{W+|es|flecha|m}}, {{W+|es|saeta|m}}
* {{Z|xsv}}: {{W-|xsv|strēla|m}}
* {{Z|sux}}: {{W-|sux|𒋾|tr=TI|sc=Xsux}}
* {{Z|sva}}: {{W-|sva|ცხვი}}
* {{Z|sw}}: {{W+|sw|mshale}}
* {{Z|sv}}: {{W+|sv|pil|g}}
* {{Z|sjw}}: {{W-|sjw|la nàhl we}}
* {{Z|xsr}}: {{W-|xsr|མདའ|tr=da}}
* {{Z|tab}}: {{W-|tab|чӏимир}}
* {{Z|tg}}: {{W+|tg|тир|sc=Cyrl}}
* {{Z|tl}}: {{W+|tl|tunod}}, {{W-|tl|palaso}}, {{W-|tl|pletsa}}
* {{Z|ta}}: {{W+|ta|அம்பு}}
* {{Z|twf}}: {{W-|twf|łúonemą}}
* {{Z|ttt}}: {{W-|ttt|ух}}
* {{Z|th}}: {{W-|th|ลูกศร}}, {{W-|th|ศร}}
* {{Z|te}}: {{W+|te|బాణము}}, {{W+|te|అమ్ము}}
* {{Z|crh}}: {{W-|crh|oq}}
* {{Z|tt}}: {{W+|tt|ук}}
* {{Z|tet}}: {{W-|tet|hana}}
* {{Z|ti}}: {{W-|ti|መንትግ|sc=Ethi}}, {{W-|ti|ኵናት|sc=Ethi}}, {{W-|ti|ፍላጻ|sc=Ethi}}
* {{Z|tr}}: {{W+|tr|ok}}
* {{Z|tk}}: {{W-|tk|ok}}, {{W-|tk|peýkam}}
* {{Z|bo}}: {{W-|bo|མདའ|sc=Tibt}}
* {{Z|tli}}: {{W-|tli|chooneit}}
* {{Z|txb}}: {{W-|txb|prere}}, {{W-|txb|tsain}}
* {{Z|tta}}: {{W-|tta|mañkoi}}, {{W-|tta|mañksii}}
* {{Z|tyv}}: {{W-|tyv|согун}}
* {{Z|udm}}: {{W-|udm|ньӧл}}
* {{Z|uga}}: {{W-|uga|𐎈𐎑}}
* {{Z|ur}}: {{W-|ur|تیر|n|tr=tīr|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|стріла́|m|sc=Cyrl}}
* {{Z|vi}}: {{W+|vi|mũi tên}}, {{W+|vi|tên}}
* {{Z|vo}}: {{W+|vo|sagit}}
* {{Z|wa}}: {{W+|wa|fritche|m}}, {{W+|wa|fletche|m}}
* {{Z|cy}}: {{W+|cy|saeth|m}}
* {{Z|wo}}: {{W+|wo|fitt}}, {{W-|wo|fett}}
* {{Z|km}}: {{W+|km|ព្រួញ}}, {{W+|km|សរ}}
* {{Z|xh}}: {{W-|xh|utolo}}
* {{Z|sah}}: {{W-|sah|ох|sc=Cyrl}}, {{W-|sah|оноҕос|sc=Cyrl}}
* {{Z|yi}}: {{W-|yi|פֿײַל|m}}
* {{Z|yo}}: {{W-|yo|o̩fà}}
* {{Z|esu}}: {{W-|esu|qeruq}}
* {{Z|el}}: {{W+|el|βέλος|nt}}, {{W+|el|σαΐτα|m}}
*: Kevn: {{W-|grc|ἰός|n}}, {{W-|grc|τόξευμα|nt}}, {{W-|grc|ὀϊστός|n}}
* {{Z|za}}: {{W-|za|naq}}, {{W-|za|cienq}}, {{W-|za|bwn}}
* {{Z|zu}}: {{W-|zu|umcibisholo|c3|c4}}
* {{Z|nmn}}: {{W-|nmn|gǁqhàã}}
{{werger-bin}}
:{{werger çavkanî|en|arrow|beş=Ev qismê Wergerê|oldid=75822606}}
=== Navdêr 2 ===
{{navdêr|ku|z=m}}
# [[erd|Erd]] û [[zevî|zeviyên]] [[teng]] û [[dirêj]].
#: '''''Tîra''' felankeso îsal beyar maye.''
==== Etîmolojî ====
{{ji?}}
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W-|en|strip fields}}
{{werger-bin}}
=== Rengdêr ===
{{ku-tewîn-rd|tîr}}
{{rengdêr|ku}}, {{t-ya nerm}}, {{r-ya nerm}}
# [[şile]]ya ne [[zelal]], [[şile]]ya [[bihêz]], [[şile]]ya [[xurt]] <br>[[şile]]ya [[kêm]] [[av]] û [[zêde]] tiştek din tê de heye
#* {{jêder|ku|jêgirtin=Xwîna ku jê dihat [[sorereş]] û '''tîr''' bû.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
#* {{jêder|ku|jêgirtin=(...) carina jî bi hemû dengan ko hungiva '''tîr''' û xwîna ron tevî hev dikirin.|{{Jêgirtin/Mehmed Uzun/Hawara Dîcleyê 1/2003}}}}
# [[sift]]; tiştên [[req]], [[hişk]]; dijberê [[ronatî]]
#* {{jêder|ku|jêgirtin=(...) di devê deriyê odê de dûmaneke '''tîr''' û tûj li ruyê wî dikeve (...)|{{Jêgirtin/Hesenê Metê/Labîrenta Cinan/1994}}}}
# [[xestî]]. Mirovên bi nexweşiya şekir ketine, xestiya glukozê di nav xwîna wan de bilind e, ji ber wê yekê xwîna wan piçek tîr e.
#* {{bêmînak|ku}}
==== Dijmane ====
* {{g|ku|ruhn}}
==== Bide ber ====
{{kol3|ku
|dijwar
|sûr
|tirş
|tûj
}}
==== Jê ====
{{kol3|ku|cure=Jê
|tîr bûn<pos:lêker>
|tîr kirin<pos:lêker>
|tîrbûn<pos:navdêr>
|tîrkirin<pos:navdêr>
|tîrbûyî<pos:rengdêr>
|tîrkirî<pos:rengdêr>
|tîrahî
|tîratî
|tîrayî
|tîrî
|tîrîtî
|tîrtî
}}
==== Etîmolojî ====
Ji {{ji|ine-pro|ku|*tegus}}, [[hevreh]]a {{hv|ps|ټينګ|tr=ṭing}}, ermenî [[թանձր]] (tencir), albanî ''[[thuk]]'', îngilîzî ''[[thick]]'', almanî ''[[dick]]'', holendî ''[[dik]]''...
==== Werger ====
{{werger-ser}}
* {{Z|ady}}: {{W-|ady|ӏужъу}}
* {{Z|de}}: {{W+|de|dick}}
* {{Z|az}}: {{W-|az|qəliz}}, {{W-|az|qatı}}
* {{Z|ba}}: {{W-|ba|ҡуйы}}
* {{Z|be}}: {{W+|be|густы́}}
* {{Z|bg}}: {{W+|bg|гъст|n}}
* {{Z|my}}: {{W+|my|ပျစ်}}
* {{Z|cs}}: {{W+|cs|hustý|n}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|濃|tr=nung4}}, {{W-|yue|稠|tr=cau4}}, {{W-|yue|滐|tr=git6}}
*: {{Z|cmn}}: {{W+|cmn|濃|tr=nóng}}, {{W+|cmn|稠|tr=chóu}}, {{W+|cmn|濃厚|tr=nónghòu}}
* {{Z|da}}: {{W+|da|tyk}}
* {{Z|id}}: {{W+|id|kental}}
* {{Z|ar}}: {{W-|ar|غَلِيظ}}
* {{Z|hy}}: {{W+|hy|թանձր}}
* {{Z|fa}}: {{W+|fa|غلیظ|tr=ğaliz}}, {{W+|fa|سفت|tr=seft}}, {{W+|fa|ستبر|tr=setabr}}, {{W+|fa|دفزک|tr=dafzak}}
* {{Z|fi}}: {{W+|fi|sakea}}, {{W+|fi|tiheä}}
* {{Z|fr}}: {{W+|fr|épais}}
* {{Z|gd}}: {{W-|gd|tiugh}}
* {{Z|gl}}: {{W+|gl|testo|n}}, {{W+|gl|espeso|n}}
* {{Z|ka}}: {{W-|ka|სქელი}}
* {{Z|ht}}: {{W-|ht|pwès}}
* {{Z|nl}}: {{W+|nl|dik}}, {{W+|nl|dikke}}
* {{Z|en}}: {{W+|en|thick}} (having viscous consistency), {{W+|en|dense}}
* {{Z|ja}}: {{W+|ja|濃い|sc=Jpan|tr=こい, koi}}
* {{Z|kbd}}: {{W-|kbd|ӏувы}}
* {{Z|ca}}: {{W+|ca|espès}}
* {{Z|ko}}: {{W+|ko|짙다}}, {{W+|ko|농후하다}}
* {{Z|mk}}: {{W-|mk|густ|n}}
* {{Z|ms}}: {{W+|ms|pekat}}
* {{Z|mnc}}: {{W-|mnc|ᡨᡠᠮᡳᠨ}}
* {{Z|hu}}: {{W+|hu|sűrű}}
* {{Z|nrf}}: {{W-|nrf|êpais}}
* {{Z|oc}}: {{W+|oc|espés}}
* {{Z|ota}}: {{W-|ota|قویو|tr=koyu}}
* {{Z|ps}}: {{W-|ps|ټينګ|tr=ṭing}}
* {{Z|pl}}: {{W+|pl|gęsty}}
* {{Z|pt}}: {{W+|pt|espesso}}
* {{Z|ru}}: {{W+|ru|густо́й}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|гу̑ст}}
*: Latînî: {{W+|sh|gȗst}}
* {{Z|sk}}: {{W-|sk|hustý}}
* {{Z|sl}}: {{W+|sl|góst}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|gusty}}
* {{Z|es}}: {{W+|es|espeso}}
* {{Z|sv}}: {{W+|sv|tjock}}
* {{Z|tg}}: {{W-|tg|ғализ}}
* {{Z|th}}: {{W+|th|ข้น}}
* {{Z|te}}: {{W+|te|చిక్కని}}
* {{Z|tr}}: {{W+|tr|yoğun}}
* {{Z|uk}}: {{W+|uk|густи́й}}
* {{Z|vi}}: {{W+|vi|đặc}}
* {{Z|cy}}: {{W+|cy|tew}}
* {{Z|za}}: {{W-|za|gwd}}, {{W-|za|gwg}}, {{W-|za|gud}}
{{werger-bin}}
:{{werger çavkanî|en|thick|beş=Ev qismê Wergerê|oldid=75873085}}
maxxx5gkzdahemoas0io67w6tbdt2qn
6277088
6277087
2026-07-24T11:29:26Z
IbrahimKocher Duhok
4280
Werger: +îngilîzî : [[strip fields]] (bi alîkarî)
6277088
wikitext
text/x-wiki
{{bnr-h|TIR|Tîr|tîr|tir|-tir}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{beş|Navdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr (navdêr).wav|Deng|Bidlîs}}
* {{beş|Rengdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr 1 ===
{{ku-tewîn-nav|tîr|mê}}
[[Wêne:WA archery target with arrows.jpg|thumb|150px|tîr]]
{{navdêr|ku|z=m}}
# {{f|ku|çek}} [[çek|Çek]]ek [[serik]][[tûj]] e ku ji [[kevan]]ê tê [[avêtin]].
#* {{jêder|ku|jêgirtin=Eger ku awir '''tîr''' bin, çav û birû jî kevan in. Di helbestan de wekî evîndar birûyên xwe xwar bike ev mîna kevanê vedikişe, tê pejirandin. Birû ji ber ku '''tîra''' herî dijwar biaveje xwe vedikişîne.|''[[w:Di Helbestên Melayê Bateyî de Evîn û Hezkirin|Di Helbestên Melayê Bateyî de Evîn û Hezkirin]]'', [[w:Reşît Rojdar Yildiz|Reşît Rojdar Yildiz]], Bingöl Üniversitesi Yaşayan Diller Enstitüsü Dergisi}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Ji wê rojê dizanî min</br>Ko yar ew '''tîr''' li cergê da</br>Gelo dîsa bibînim ez}}
#: ''Reş e weke qîrê,</br>Rast e weke '''tîrê'''.''
==== Bi alfabeyên din ====
* {{ku-ar|تیر}}
==== Bide ber ====
* {{g|ku|rim}}
* {{g|ku|peygan}}
==== Jê ====
{{kol3|ku|cure=Jê
|sertîrik
|sertîrikî
|tîravêj
|tîravêjî
|tîrik
|tîrk
}}
==== Etîmolojî ====
Hevreha soranî {{g|ckb|تیر|tr=tîr}}, farisî {{g|fa|تیر|tr=tîr}}, tacikî [[тир]] (tîr), pehlewî ''tigr'', avestayî ''tiẍre-, tiẍri-'', ji {{ji|ira}}. Herwiha bide ber sûmerî [[𒋾]] (ti: tîr)
==== Binêre herwiha ====
{{lîste:çek/ku}}
==== Bi zaravayên din ====
* Soranî: {{W-|ckb|تیر|tr=tîr}}
==== Werger ====
{{werger-ser|tîr wek çek yan nîşan}}
* {{Z|abq}}: {{W-|abq|сагӏындакъ}}
* {{Z|ab}}: {{W-|ab|ахәымпал|sc=Cyrl}}
* {{Z|ady}}: {{W-|ady|щэбзащ}}
* {{Z|af}}: {{W-|af|pyl}}, {{W-|af|skig}}
* {{Z|ak}}: {{W-|ak|agyan}}
* {{Z|sq}}: {{W+|sq|shigjetë|m}}, {{W+|sq|ber|n}}
* {{Z|de}}: {{W+|de|Pfeil|n}}
* {{Z|amn}}: {{W-|amn|fak}}
* {{Z|am}}: {{W-|am|ፍላጣ|sc=Ethi}}, {{W-|am|ቀስት|sc=Ethi}}, {{W-|am|ፍላጻ|sc=Ethi}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|k’aa’ist'aaní}}
* {{Z|arc}}:
*: {{Z|syc}}: {{W-|syc|ܓܐܪܐ|tr=giʾrā|sc=Syrc}}
* {{Z|arw}}: {{W-|arw|simala}}
* {{Z|rup}}: {{W-|rup|sãdzeatã}}
* {{Z|as}}: {{W-|as|কাঁড়}}
* {{Z|ast}}: {{W-|ast|flecha|m}}
* {{Z|av}}: {{W-|av|йойохул гӏучӏ|sc=Cyrl}}, {{W-|av|чӏор|sc=Cyrl}}
* {{Z|ae}}: {{W-|ae|𐬙𐬌𐬔𐬭𐬌}}
* {{Z|ay}}: {{W-|ay|mich'i}}
* {{Z|az}}: {{W+|az|ox}}
*: Kirîlî: {{W-|az|ох|sc=Cyrl}}
*: {{Z|az}}: {{W-|az|اوخ|tr=ox|sc=fa-Arab}}
* {{Z|eu}}: {{W+|eu|gezi}}
* {{Z|ba}}: {{W-|ba|уҡ|sc=Cyrl}}
* {{Z|be}}: {{W-|be|страла́|m|sc=Cyrl}}
* {{Z|bn}}: {{W+|bn|তীর|sc=Beng}}
* {{Z|bi}}: {{W-|bi|ara}}
* {{Z|bor}}: {{W-|bor|tugo}}
* {{Z|bg}}: {{W+|bg|стрела́|m|sc=Cyrl}}
* {{Z|my}}: {{W+|my|မြား|sc=Mymr}}
* {{Z|ccc}}: {{W-|ccc|ashkololi}}
* {{Z|ce}}: {{W-|ce|пха}}
* {{Z|cs}}: {{W+|cs|šíp|n}}, {{W+|cs|střela|m}}
* {{Z|chr}}: {{W-|chr|ᎫᏂ}}
* {{Z|chy}}: {{W-|chy|maahe}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|箭|tr=zin3}}, {{W-|yue|矢|tr=ci2}}
*: {{Z|dng}}: {{W-|dng|җян}}
*: {{Z|cmn}}: {{W+|cmn|箭|tr=jiàn}}, {{W+|cmn|矢|tr=shǐ}}, {{W+|cmn|箭頭|tr=jiàntóu}}
*: {{Z|nan}}: {{W-|nan|箭|tr=chìⁿ}}, {{W-|nan|矢|tr=sí}}
*: {{Z|wuu}}: {{W-|wuu|箭}}, {{W-|wuu|矢}}
* {{Z|chp}}: {{W-|chp|k’á}}
* {{Z|cho}}: {{W-|cho|oo-ske-noc-kay}}
* {{Z|cv}}: {{W-|cv|ҫӗмрен|sc=Cyrl}}
* {{Z|da}}: {{W-|da|pil|g}}
* {{Z|dar}}: {{W-|dar|урги}}
* {{Z|dv}}: {{W-|dv|ތީރު|sc=Thaa}}
* {{Z|sce}}: {{W-|sce|sumu}}
* {{Z|ovd}}: {{W-|ovd|kuov|n}}
* {{Z|id}}: {{W+|id|panah}}
* {{Z|ar}}: {{W+|ar|سَهْم|n}}
*: {{Z|arz}}: {{W-|arz|سهم|n|tr=sahm}}
*: {{Z|acw}}: {{W-|acw|سَهَم|n|tr=saham}}
* {{Z|hy}}: {{W+|hy|նետ}}
* {{Z|myv}}: {{W-|myv|нал}}, {{W-|myv|нал}}
* {{Z|eo}}: {{W+|eo|sago}}
* {{Z|et}}: {{W+|et|nool}}
* {{Z|evn}}: {{W-|evn|нюр}}
* {{Z|ee}}: {{W-|ee|aŋutrɔ|nt}}
* {{Z|pal}}: {{W-|pal|𐭲𐭢𐭫|ts=tigr}}
* {{Z|fa}}: {{W+|fa|تیر|tr=tir}}
* {{Z|fo}}: {{W-|fo|ørvur|n}}, {{W-|fo|pílur|n}}, {{W-|fo|ørv|m}}
* {{Z|fi}}: {{W+|fi|nuoli}}
* {{Z|fr}}: {{W+|fr|flèche|m}}
* {{Z|fur}}: {{W-|fur|frece|m}}
* {{Z|stq}}: {{W-|stq|Stroal|n}}
* {{Z|fy}}: {{W-|fy|pylk|nt}}
* {{Z|gd}}: {{W-|gd|saighead|m}}, {{W-|gd|gath|m}}
* {{Z|gl}}: {{W-|gl|frecha|m}}, {{W-|gl|frisga|m}}, {{W+|gl|seta|m}}
* {{Z|got}}: {{W-|got|𐌰𐍂𐍈𐌰𐌶𐌽𐌰|m}}
* {{Z|gu}}: {{W-|gu|બાણ|sc=Gujr}}
* {{Z|ka}}: {{W-|ka|ისარი|sc=Geor}}
* {{Z|haw}}: {{W-|haw|pua}}, {{W-|haw|pua pana}}
* {{Z|ha}}: {{W-|ha|kibiya}}
* {{Z|hi}}: {{W+|hi|तीर|n}}, {{W+|hi|बाण|sc=Deva}}
* {{Z|nl}}: {{W+|nl|pijl|n}}
* {{Z|hrx}}: {{W-|hrx|Peil|n}}
* {{Z|he}}: {{W+|he|חֵץ|n|tr=khets|sc=Hebr}}
* {{Z|io}}: {{W+|io|flecho}}
* {{Z|ig}}: {{W-|ig|arọ}}
* {{Z|ang}}: {{W-|ang|flān|n}}
* {{Z|en}}: {{W+|en|arrow}}
* {{Z|inh}}: {{W-|inh|тӏаьск}}
* {{Z|ia}}: {{W-|ia|flecha}}, {{W-|ia|sagitta}}
* {{Z|ga}}: {{W-|ga|saighead|m}}
* {{Z|zai}}: {{W-|zai|baxa}}, {{W-|zai|baza}}
* {{Z|it}}: {{W+|it|freccia|m}}
* {{Z|is}}: {{W+|is|ör|m}}
* {{Z|ja}}: {{W+|ja|矢|tr=や, ya}}
* {{Z|jra}}: {{W-|jra|ƀrom}}
* {{Z|kaw}}: {{W-|kaw|panah}}
* {{Z|jv}}: {{W+|jv|ꦥꦤꦃ}}
* {{Z|kbd}}: {{W-|kbd|шабзэ}}
* {{Z|kl}}: {{W-|kl|qarsoq}}
* {{Z|kn}}: {{W+|kn|ಅಂಬು|sc=Knda}}, {{W+|kn|ಬಾಣ|sc=Knda}}
* {{Z|krc}}: {{W-|krc|окъ|sc=Cyrl}}
* {{Z|krl}}: {{W-|krl|piili}}
* {{Z|ca}}: {{W+|ca|fletxa|m}}, {{W+|ca|sageta|m}}
* {{Z|qu}}: {{W-|qu|wach'i}}
* {{Z|ket}}: {{W-|ket|ӄам}}
* {{Z|ky}}: {{W+|ky|жебе|sc=Cyrl}}
* {{Z|ki}}: {{W-|ki|mũguĩ|c3}}
* {{Z|si}}: {{W-|si|ඊය|sc=Sinh}}
* {{Z|koi}}: {{W-|koi|ньӧв}}
* {{Z|ko}}: {{W+|ko|화살}}
* {{Z|kw}}: {{W-|kw|seth}}
* {{Z|kum}}: {{W-|kum|окъ}}
* {{Z|lbe}}: {{W-|lbe|чӏатӏаракӏ}}
* {{Z|lkt}}: {{W-|lkt|waŋhíŋkpe}}
* {{Z|ltg}}: {{W-|ltg|šautra|m}}
* {{Z|la}}: {{W+|la|sagitta|m}}
* {{Z|lv}}: {{W-|lv|bulta|m}}, {{W-|lv|šautra|m}}, {{W-|lv|strēle|m}}
* {{Z|lo}}: {{W-|lo|ລູກສອນ}}, {{W-|lo|ສອນ}}
* {{Z|ln}}: {{W-|ln|likulá}}
* {{Z|lt}}: {{W-|lt|strėlė|m}}
* {{Z|khb}}: {{W-|khb|ᦔᦹᧃ|sc=Talu}}, {{W-|khb|ᦐᦱᧉᦺᦙᧉ}}
* {{Z|lb}}: {{W-|lb|Fäil|n}}
* {{Z|mk}}: {{W-|mk|стрела|m|sc=Cyrl}}
* {{Z|ml}}: {{W+|ml|അമ്പ്|sc=Mlym}}
* {{Z|ms}}: {{W+|ms|panah}}
*: {{Z|ms}}: {{W-|ms|ڤانه|sc=Arab}}
* {{Z|mt}}: {{W-|mt|vleġġa|m}}
* {{Z|mnc}}: {{W-|mnc|ᠰᡳᡵᡩᠠᠨ}}, {{W-|mnc|ᠨᡳᡵᡠ}}
* {{Z|mns-nor}}: {{W-|mns-nor|ня̄л}}
* {{Z|mi}}: {{W-|mi|kōpere}}, {{W-|mi|pere}}
* {{Z|mr}}: {{W-|mr|बाण}}
* Marî:
*: {{Z|mhr}}: {{W-|mhr|пикш}}
* {{Z|hu}}: {{W+|hu|nyíl}}
* {{Z|egy}}: {{W-egy|šsr|n|h=V6-s-r:T11}}, {{W-egy|zwn|n|h=z-wn:n-T11}}
* {{Z|moh}}: {{W-|moh|kayonkwere}}
* {{Z|mdf}}: {{W-|mdf|нал}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|сум}}
*: Mongolî: {{W-|mn|ᠰᠤᠮᠤ}}
* {{Z|mnw}}: {{W-|mnw|လဴ}}
* {{Z|nci}}: {{W-|nci|mitl}}
* {{Z|nah}}: {{W+|nah|mitl}}
* {{Z|nnt}}: {{W-|nnt|allontz}}
* {{Z|nv}}: {{W-|nv|kʼaaʼ}}
* {{Z|nds}}: {{W-|nds|Piel|n}}, {{W-|nds|Piil|n}}
* {{Z|non}}: {{W-|non|ǫr|m}}
* {{Z|no}}: {{W+|no|pil}}
*: {{Z|nb}}: {{W+|nb|pil|n|m}}
*: {{Z|nn}}: {{W-|nn|pil|m}}
* {{Z|nwy}}: {{W-|nwy|aruntquaserank}}
* {{Z|oj}}: {{W-|oj|mitigwanwi}}, {{W-|oj|bikwak}}, {{W-|oj|ginwaakwanwi}}, {{W-|oj|gii'anwaakonwi}}, {{W-|oj|asawaan}}
* {{Z|oc}}: {{W+|oc|flècha|m}}, {{W+|oc|sageta|m}}
* {{Z|one}}: {{W-|one|kayú:kwile̲’}}
* {{Z|or}}: {{W+|or|ତୀର}}
* {{Z|os}}: {{W-|os|фат|sc=Cyrl}}
* {{Z|ota}}: {{W-|ota|اوق|tr=ok}}, {{W-|ota|تیر|tr=tir}}, {{W-|ota|سهم|tr=sehm}}
* {{Z|ug}}: {{W-|ug|ئوق|sc=ug-Arab}}
* {{Z|uz}}: {{W+|uz|oʻq}}, {{W+|uz|tir}}
* {{Z|pi}}: {{W-|pi|usu}}, {{W-|pi|sara|n}}, {{W-|pi|bāṇa|n}}
* {{Z|ps}}: {{W-|ps|غشۍ|tr=ǧašëy|sc=ps-Arab}}
* {{Z|myp}}: {{W-|myp|kahaí}}
* {{Z|pdt}}: {{W-|pdt|Feil|m}}
* {{Z|pl}}: {{W+|pl|strzała|m}}, {{W-|pl|szyp|n}}
* {{Z|roa-opt}}: {{W-|roa-opt|saeta}}
* {{Z|pt}}: {{W+|pt|flecha|m}}, {{W+|pt|seta|m}}
* {{Z|pa}}: {{W-|pa|ਤੀਰ}}
* {{Z|kk}}: {{W-|kk|жебе|sc=Cyrl}}, {{W-|kk|оқ|sc=Cyrl}}
* {{Z|mwr}}: {{W-|mwr|तीर|tr=tīr}}
* {{Z|rhg}}: {{W-|rhg|tir}}
* {{Z|rm}}: {{W-|rm|frizza|m}}
* {{Z|ro}}: {{W+|ro|săgeată|m}}
*: Kirîlî: {{W-|ro|сэӂатэ|m}}
* {{Z|ru}}: {{W+|ru|стрела́|m}}
* {{Z|se}}: {{W-|se|njuolla}}
* {{Z|sa}}: {{W+|sa|इषु|n}}, {{W+|sa|बाण}}, {{W+|sa|शर|n}}
* {{Z|scn}}: {{W-|scn|freccia|m}}
* {{Z|sd}}: {{W+|sd|پيلو|tr=pīlō|cuda=پيِلو|sc=sd-Arab}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|стрела|m}}, {{W-|sh|стријела|m}}, {{W-|sh|стрелица|m}}, {{W-|sh|стрјелица|m}}, {{W-|sh|стрила|m}}
*: Latînî: {{W-|sh|strela|m}}, {{W+|sh|strijela|m}}, {{W+|sh|strelica|m}}, {{W-|sh|strjelica|m}}, {{W-|sh|strila|m}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|стрѣла|m|sc=Cyrs}}
*: Glagolîtîk: {{W-|cu|ⱄⱅⱃⱑⰾⰰ|m|sc=Glag}}
* {{Z|orv}}: {{W-|orv|стрѣла|m}}
* {{Z|sk}}: {{W-|sk|šíp}}, {{W-|sk|strela|m}}
* {{Z|sl}}: {{W+|sl|puščica|m}}
* {{Z|zlw-slv}}: {{W-|zlw-slv|střåula|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|klek|n}}, {{W-|dsb|šypa|m}}
*: {{Z|hsb}}: {{W-|hsb|kłok|n}}, {{W-|hsb|šip|n}}
* {{Z|st}}: {{W-|st|motsu}}
* {{Z|es}}: {{W+|es|flecha|m}}, {{W+|es|saeta|m}}
* {{Z|xsv}}: {{W-|xsv|strēla|m}}
* {{Z|sux}}: {{W-|sux|𒋾|tr=TI|sc=Xsux}}
* {{Z|sva}}: {{W-|sva|ცხვი}}
* {{Z|sw}}: {{W+|sw|mshale}}
* {{Z|sv}}: {{W+|sv|pil|g}}
* {{Z|sjw}}: {{W-|sjw|la nàhl we}}
* {{Z|xsr}}: {{W-|xsr|མདའ|tr=da}}
* {{Z|tab}}: {{W-|tab|чӏимир}}
* {{Z|tg}}: {{W+|tg|тир|sc=Cyrl}}
* {{Z|tl}}: {{W+|tl|tunod}}, {{W-|tl|palaso}}, {{W-|tl|pletsa}}
* {{Z|ta}}: {{W+|ta|அம்பு}}
* {{Z|twf}}: {{W-|twf|łúonemą}}
* {{Z|ttt}}: {{W-|ttt|ух}}
* {{Z|th}}: {{W-|th|ลูกศร}}, {{W-|th|ศร}}
* {{Z|te}}: {{W+|te|బాణము}}, {{W+|te|అమ్ము}}
* {{Z|crh}}: {{W-|crh|oq}}
* {{Z|tt}}: {{W+|tt|ук}}
* {{Z|tet}}: {{W-|tet|hana}}
* {{Z|ti}}: {{W-|ti|መንትግ|sc=Ethi}}, {{W-|ti|ኵናት|sc=Ethi}}, {{W-|ti|ፍላጻ|sc=Ethi}}
* {{Z|tr}}: {{W+|tr|ok}}
* {{Z|tk}}: {{W-|tk|ok}}, {{W-|tk|peýkam}}
* {{Z|bo}}: {{W-|bo|མདའ|sc=Tibt}}
* {{Z|tli}}: {{W-|tli|chooneit}}
* {{Z|txb}}: {{W-|txb|prere}}, {{W-|txb|tsain}}
* {{Z|tta}}: {{W-|tta|mañkoi}}, {{W-|tta|mañksii}}
* {{Z|tyv}}: {{W-|tyv|согун}}
* {{Z|udm}}: {{W-|udm|ньӧл}}
* {{Z|uga}}: {{W-|uga|𐎈𐎑}}
* {{Z|ur}}: {{W-|ur|تیر|n|tr=tīr|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|стріла́|m|sc=Cyrl}}
* {{Z|vi}}: {{W+|vi|mũi tên}}, {{W+|vi|tên}}
* {{Z|vo}}: {{W+|vo|sagit}}
* {{Z|wa}}: {{W+|wa|fritche|m}}, {{W+|wa|fletche|m}}
* {{Z|cy}}: {{W+|cy|saeth|m}}
* {{Z|wo}}: {{W+|wo|fitt}}, {{W-|wo|fett}}
* {{Z|km}}: {{W+|km|ព្រួញ}}, {{W+|km|សរ}}
* {{Z|xh}}: {{W-|xh|utolo}}
* {{Z|sah}}: {{W-|sah|ох|sc=Cyrl}}, {{W-|sah|оноҕос|sc=Cyrl}}
* {{Z|yi}}: {{W-|yi|פֿײַל|m}}
* {{Z|yo}}: {{W-|yo|o̩fà}}
* {{Z|esu}}: {{W-|esu|qeruq}}
* {{Z|el}}: {{W+|el|βέλος|nt}}, {{W+|el|σαΐτα|m}}
*: Kevn: {{W-|grc|ἰός|n}}, {{W-|grc|τόξευμα|nt}}, {{W-|grc|ὀϊστός|n}}
* {{Z|za}}: {{W-|za|naq}}, {{W-|za|cienq}}, {{W-|za|bwn}}
* {{Z|zu}}: {{W-|zu|umcibisholo|c3|c4}}
* {{Z|nmn}}: {{W-|nmn|gǁqhàã}}
{{werger-bin}}
:{{werger çavkanî|en|arrow|beş=Ev qismê Wergerê|oldid=75822606}}
=== Navdêr 2 ===
{{navdêr|ku|z=m}}
# [[erd|Erd]] û [[zevî|zeviyên]] [[teng]] û [[dirêj]].
#: '''''Tîra''' felankeso îsal beyar maye.''
==== Etîmolojî ====
{{ji?}}
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W-|en|strip fields}}, {{W-|en|strip fields}}
{{werger-bin}}
=== Rengdêr ===
{{ku-tewîn-rd|tîr}}
{{rengdêr|ku}}, {{t-ya nerm}}, {{r-ya nerm}}
# [[şile]]ya ne [[zelal]], [[şile]]ya [[bihêz]], [[şile]]ya [[xurt]] <br>[[şile]]ya [[kêm]] [[av]] û [[zêde]] tiştek din tê de heye
#* {{jêder|ku|jêgirtin=Xwîna ku jê dihat [[sorereş]] û '''tîr''' bû.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
#* {{jêder|ku|jêgirtin=(...) carina jî bi hemû dengan ko hungiva '''tîr''' û xwîna ron tevî hev dikirin.|{{Jêgirtin/Mehmed Uzun/Hawara Dîcleyê 1/2003}}}}
# [[sift]]; tiştên [[req]], [[hişk]]; dijberê [[ronatî]]
#* {{jêder|ku|jêgirtin=(...) di devê deriyê odê de dûmaneke '''tîr''' û tûj li ruyê wî dikeve (...)|{{Jêgirtin/Hesenê Metê/Labîrenta Cinan/1994}}}}
# [[xestî]]. Mirovên bi nexweşiya şekir ketine, xestiya glukozê di nav xwîna wan de bilind e, ji ber wê yekê xwîna wan piçek tîr e.
#* {{bêmînak|ku}}
==== Dijmane ====
* {{g|ku|ruhn}}
==== Bide ber ====
{{kol3|ku
|dijwar
|sûr
|tirş
|tûj
}}
==== Jê ====
{{kol3|ku|cure=Jê
|tîr bûn<pos:lêker>
|tîr kirin<pos:lêker>
|tîrbûn<pos:navdêr>
|tîrkirin<pos:navdêr>
|tîrbûyî<pos:rengdêr>
|tîrkirî<pos:rengdêr>
|tîrahî
|tîratî
|tîrayî
|tîrî
|tîrîtî
|tîrtî
}}
==== Etîmolojî ====
Ji {{ji|ine-pro|ku|*tegus}}, [[hevreh]]a {{hv|ps|ټينګ|tr=ṭing}}, ermenî [[թանձր]] (tencir), albanî ''[[thuk]]'', îngilîzî ''[[thick]]'', almanî ''[[dick]]'', holendî ''[[dik]]''...
==== Werger ====
{{werger-ser}}
* {{Z|ady}}: {{W-|ady|ӏужъу}}
* {{Z|de}}: {{W+|de|dick}}
* {{Z|az}}: {{W-|az|qəliz}}, {{W-|az|qatı}}
* {{Z|ba}}: {{W-|ba|ҡуйы}}
* {{Z|be}}: {{W+|be|густы́}}
* {{Z|bg}}: {{W+|bg|гъст|n}}
* {{Z|my}}: {{W+|my|ပျစ်}}
* {{Z|cs}}: {{W+|cs|hustý|n}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|濃|tr=nung4}}, {{W-|yue|稠|tr=cau4}}, {{W-|yue|滐|tr=git6}}
*: {{Z|cmn}}: {{W+|cmn|濃|tr=nóng}}, {{W+|cmn|稠|tr=chóu}}, {{W+|cmn|濃厚|tr=nónghòu}}
* {{Z|da}}: {{W+|da|tyk}}
* {{Z|id}}: {{W+|id|kental}}
* {{Z|ar}}: {{W-|ar|غَلِيظ}}
* {{Z|hy}}: {{W+|hy|թանձր}}
* {{Z|fa}}: {{W+|fa|غلیظ|tr=ğaliz}}, {{W+|fa|سفت|tr=seft}}, {{W+|fa|ستبر|tr=setabr}}, {{W+|fa|دفزک|tr=dafzak}}
* {{Z|fi}}: {{W+|fi|sakea}}, {{W+|fi|tiheä}}
* {{Z|fr}}: {{W+|fr|épais}}
* {{Z|gd}}: {{W-|gd|tiugh}}
* {{Z|gl}}: {{W+|gl|testo|n}}, {{W+|gl|espeso|n}}
* {{Z|ka}}: {{W-|ka|სქელი}}
* {{Z|ht}}: {{W-|ht|pwès}}
* {{Z|nl}}: {{W+|nl|dik}}, {{W+|nl|dikke}}
* {{Z|en}}: {{W+|en|thick}} (having viscous consistency), {{W+|en|dense}}
* {{Z|ja}}: {{W+|ja|濃い|sc=Jpan|tr=こい, koi}}
* {{Z|kbd}}: {{W-|kbd|ӏувы}}
* {{Z|ca}}: {{W+|ca|espès}}
* {{Z|ko}}: {{W+|ko|짙다}}, {{W+|ko|농후하다}}
* {{Z|mk}}: {{W-|mk|густ|n}}
* {{Z|ms}}: {{W+|ms|pekat}}
* {{Z|mnc}}: {{W-|mnc|ᡨᡠᠮᡳᠨ}}
* {{Z|hu}}: {{W+|hu|sűrű}}
* {{Z|nrf}}: {{W-|nrf|êpais}}
* {{Z|oc}}: {{W+|oc|espés}}
* {{Z|ota}}: {{W-|ota|قویو|tr=koyu}}
* {{Z|ps}}: {{W-|ps|ټينګ|tr=ṭing}}
* {{Z|pl}}: {{W+|pl|gęsty}}
* {{Z|pt}}: {{W+|pt|espesso}}
* {{Z|ru}}: {{W+|ru|густо́й}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|гу̑ст}}
*: Latînî: {{W+|sh|gȗst}}
* {{Z|sk}}: {{W-|sk|hustý}}
* {{Z|sl}}: {{W+|sl|góst}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|gusty}}
* {{Z|es}}: {{W+|es|espeso}}
* {{Z|sv}}: {{W+|sv|tjock}}
* {{Z|tg}}: {{W-|tg|ғализ}}
* {{Z|th}}: {{W+|th|ข้น}}
* {{Z|te}}: {{W+|te|చిక్కని}}
* {{Z|tr}}: {{W+|tr|yoğun}}
* {{Z|uk}}: {{W+|uk|густи́й}}
* {{Z|vi}}: {{W+|vi|đặc}}
* {{Z|cy}}: {{W+|cy|tew}}
* {{Z|za}}: {{W-|za|gwd}}, {{W-|za|gwg}}, {{W-|za|gud}}
{{werger-bin}}
:{{werger çavkanî|en|thick|beş=Ev qismê Wergerê|oldid=75873085}}
aaarlr7rl05dlep54uktbjdh1rcu45s
6277089
6277088
2026-07-24T11:29:50Z
IbrahimKocher Duhok
4280
Werger: +îngilîzî : [[strip holdings]] (bi alîkarî)
6277089
wikitext
text/x-wiki
{{bnr-h|TIR|Tîr|tîr|tir|-tir}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{beş|Navdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr (navdêr).wav|Deng|Bidlîs}}
* {{beş|Rengdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr 1 ===
{{ku-tewîn-nav|tîr|mê}}
[[Wêne:WA archery target with arrows.jpg|thumb|150px|tîr]]
{{navdêr|ku|z=m}}
# {{f|ku|çek}} [[çek|Çek]]ek [[serik]][[tûj]] e ku ji [[kevan]]ê tê [[avêtin]].
#* {{jêder|ku|jêgirtin=Eger ku awir '''tîr''' bin, çav û birû jî kevan in. Di helbestan de wekî evîndar birûyên xwe xwar bike ev mîna kevanê vedikişe, tê pejirandin. Birû ji ber ku '''tîra''' herî dijwar biaveje xwe vedikişîne.|''[[w:Di Helbestên Melayê Bateyî de Evîn û Hezkirin|Di Helbestên Melayê Bateyî de Evîn û Hezkirin]]'', [[w:Reşît Rojdar Yildiz|Reşît Rojdar Yildiz]], Bingöl Üniversitesi Yaşayan Diller Enstitüsü Dergisi}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Ji wê rojê dizanî min</br>Ko yar ew '''tîr''' li cergê da</br>Gelo dîsa bibînim ez}}
#: ''Reş e weke qîrê,</br>Rast e weke '''tîrê'''.''
==== Bi alfabeyên din ====
* {{ku-ar|تیر}}
==== Bide ber ====
* {{g|ku|rim}}
* {{g|ku|peygan}}
==== Jê ====
{{kol3|ku|cure=Jê
|sertîrik
|sertîrikî
|tîravêj
|tîravêjî
|tîrik
|tîrk
}}
==== Etîmolojî ====
Hevreha soranî {{g|ckb|تیر|tr=tîr}}, farisî {{g|fa|تیر|tr=tîr}}, tacikî [[тир]] (tîr), pehlewî ''tigr'', avestayî ''tiẍre-, tiẍri-'', ji {{ji|ira}}. Herwiha bide ber sûmerî [[𒋾]] (ti: tîr)
==== Binêre herwiha ====
{{lîste:çek/ku}}
==== Bi zaravayên din ====
* Soranî: {{W-|ckb|تیر|tr=tîr}}
==== Werger ====
{{werger-ser|tîr wek çek yan nîşan}}
* {{Z|abq}}: {{W-|abq|сагӏындакъ}}
* {{Z|ab}}: {{W-|ab|ахәымпал|sc=Cyrl}}
* {{Z|ady}}: {{W-|ady|щэбзащ}}
* {{Z|af}}: {{W-|af|pyl}}, {{W-|af|skig}}
* {{Z|ak}}: {{W-|ak|agyan}}
* {{Z|sq}}: {{W+|sq|shigjetë|m}}, {{W+|sq|ber|n}}
* {{Z|de}}: {{W+|de|Pfeil|n}}
* {{Z|amn}}: {{W-|amn|fak}}
* {{Z|am}}: {{W-|am|ፍላጣ|sc=Ethi}}, {{W-|am|ቀስት|sc=Ethi}}, {{W-|am|ፍላጻ|sc=Ethi}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|k’aa’ist'aaní}}
* {{Z|arc}}:
*: {{Z|syc}}: {{W-|syc|ܓܐܪܐ|tr=giʾrā|sc=Syrc}}
* {{Z|arw}}: {{W-|arw|simala}}
* {{Z|rup}}: {{W-|rup|sãdzeatã}}
* {{Z|as}}: {{W-|as|কাঁড়}}
* {{Z|ast}}: {{W-|ast|flecha|m}}
* {{Z|av}}: {{W-|av|йойохул гӏучӏ|sc=Cyrl}}, {{W-|av|чӏор|sc=Cyrl}}
* {{Z|ae}}: {{W-|ae|𐬙𐬌𐬔𐬭𐬌}}
* {{Z|ay}}: {{W-|ay|mich'i}}
* {{Z|az}}: {{W+|az|ox}}
*: Kirîlî: {{W-|az|ох|sc=Cyrl}}
*: {{Z|az}}: {{W-|az|اوخ|tr=ox|sc=fa-Arab}}
* {{Z|eu}}: {{W+|eu|gezi}}
* {{Z|ba}}: {{W-|ba|уҡ|sc=Cyrl}}
* {{Z|be}}: {{W-|be|страла́|m|sc=Cyrl}}
* {{Z|bn}}: {{W+|bn|তীর|sc=Beng}}
* {{Z|bi}}: {{W-|bi|ara}}
* {{Z|bor}}: {{W-|bor|tugo}}
* {{Z|bg}}: {{W+|bg|стрела́|m|sc=Cyrl}}
* {{Z|my}}: {{W+|my|မြား|sc=Mymr}}
* {{Z|ccc}}: {{W-|ccc|ashkololi}}
* {{Z|ce}}: {{W-|ce|пха}}
* {{Z|cs}}: {{W+|cs|šíp|n}}, {{W+|cs|střela|m}}
* {{Z|chr}}: {{W-|chr|ᎫᏂ}}
* {{Z|chy}}: {{W-|chy|maahe}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|箭|tr=zin3}}, {{W-|yue|矢|tr=ci2}}
*: {{Z|dng}}: {{W-|dng|җян}}
*: {{Z|cmn}}: {{W+|cmn|箭|tr=jiàn}}, {{W+|cmn|矢|tr=shǐ}}, {{W+|cmn|箭頭|tr=jiàntóu}}
*: {{Z|nan}}: {{W-|nan|箭|tr=chìⁿ}}, {{W-|nan|矢|tr=sí}}
*: {{Z|wuu}}: {{W-|wuu|箭}}, {{W-|wuu|矢}}
* {{Z|chp}}: {{W-|chp|k’á}}
* {{Z|cho}}: {{W-|cho|oo-ske-noc-kay}}
* {{Z|cv}}: {{W-|cv|ҫӗмрен|sc=Cyrl}}
* {{Z|da}}: {{W-|da|pil|g}}
* {{Z|dar}}: {{W-|dar|урги}}
* {{Z|dv}}: {{W-|dv|ތީރު|sc=Thaa}}
* {{Z|sce}}: {{W-|sce|sumu}}
* {{Z|ovd}}: {{W-|ovd|kuov|n}}
* {{Z|id}}: {{W+|id|panah}}
* {{Z|ar}}: {{W+|ar|سَهْم|n}}
*: {{Z|arz}}: {{W-|arz|سهم|n|tr=sahm}}
*: {{Z|acw}}: {{W-|acw|سَهَم|n|tr=saham}}
* {{Z|hy}}: {{W+|hy|նետ}}
* {{Z|myv}}: {{W-|myv|нал}}, {{W-|myv|нал}}
* {{Z|eo}}: {{W+|eo|sago}}
* {{Z|et}}: {{W+|et|nool}}
* {{Z|evn}}: {{W-|evn|нюр}}
* {{Z|ee}}: {{W-|ee|aŋutrɔ|nt}}
* {{Z|pal}}: {{W-|pal|𐭲𐭢𐭫|ts=tigr}}
* {{Z|fa}}: {{W+|fa|تیر|tr=tir}}
* {{Z|fo}}: {{W-|fo|ørvur|n}}, {{W-|fo|pílur|n}}, {{W-|fo|ørv|m}}
* {{Z|fi}}: {{W+|fi|nuoli}}
* {{Z|fr}}: {{W+|fr|flèche|m}}
* {{Z|fur}}: {{W-|fur|frece|m}}
* {{Z|stq}}: {{W-|stq|Stroal|n}}
* {{Z|fy}}: {{W-|fy|pylk|nt}}
* {{Z|gd}}: {{W-|gd|saighead|m}}, {{W-|gd|gath|m}}
* {{Z|gl}}: {{W-|gl|frecha|m}}, {{W-|gl|frisga|m}}, {{W+|gl|seta|m}}
* {{Z|got}}: {{W-|got|𐌰𐍂𐍈𐌰𐌶𐌽𐌰|m}}
* {{Z|gu}}: {{W-|gu|બાણ|sc=Gujr}}
* {{Z|ka}}: {{W-|ka|ისარი|sc=Geor}}
* {{Z|haw}}: {{W-|haw|pua}}, {{W-|haw|pua pana}}
* {{Z|ha}}: {{W-|ha|kibiya}}
* {{Z|hi}}: {{W+|hi|तीर|n}}, {{W+|hi|बाण|sc=Deva}}
* {{Z|nl}}: {{W+|nl|pijl|n}}
* {{Z|hrx}}: {{W-|hrx|Peil|n}}
* {{Z|he}}: {{W+|he|חֵץ|n|tr=khets|sc=Hebr}}
* {{Z|io}}: {{W+|io|flecho}}
* {{Z|ig}}: {{W-|ig|arọ}}
* {{Z|ang}}: {{W-|ang|flān|n}}
* {{Z|en}}: {{W+|en|arrow}}
* {{Z|inh}}: {{W-|inh|тӏаьск}}
* {{Z|ia}}: {{W-|ia|flecha}}, {{W-|ia|sagitta}}
* {{Z|ga}}: {{W-|ga|saighead|m}}
* {{Z|zai}}: {{W-|zai|baxa}}, {{W-|zai|baza}}
* {{Z|it}}: {{W+|it|freccia|m}}
* {{Z|is}}: {{W+|is|ör|m}}
* {{Z|ja}}: {{W+|ja|矢|tr=や, ya}}
* {{Z|jra}}: {{W-|jra|ƀrom}}
* {{Z|kaw}}: {{W-|kaw|panah}}
* {{Z|jv}}: {{W+|jv|ꦥꦤꦃ}}
* {{Z|kbd}}: {{W-|kbd|шабзэ}}
* {{Z|kl}}: {{W-|kl|qarsoq}}
* {{Z|kn}}: {{W+|kn|ಅಂಬು|sc=Knda}}, {{W+|kn|ಬಾಣ|sc=Knda}}
* {{Z|krc}}: {{W-|krc|окъ|sc=Cyrl}}
* {{Z|krl}}: {{W-|krl|piili}}
* {{Z|ca}}: {{W+|ca|fletxa|m}}, {{W+|ca|sageta|m}}
* {{Z|qu}}: {{W-|qu|wach'i}}
* {{Z|ket}}: {{W-|ket|ӄам}}
* {{Z|ky}}: {{W+|ky|жебе|sc=Cyrl}}
* {{Z|ki}}: {{W-|ki|mũguĩ|c3}}
* {{Z|si}}: {{W-|si|ඊය|sc=Sinh}}
* {{Z|koi}}: {{W-|koi|ньӧв}}
* {{Z|ko}}: {{W+|ko|화살}}
* {{Z|kw}}: {{W-|kw|seth}}
* {{Z|kum}}: {{W-|kum|окъ}}
* {{Z|lbe}}: {{W-|lbe|чӏатӏаракӏ}}
* {{Z|lkt}}: {{W-|lkt|waŋhíŋkpe}}
* {{Z|ltg}}: {{W-|ltg|šautra|m}}
* {{Z|la}}: {{W+|la|sagitta|m}}
* {{Z|lv}}: {{W-|lv|bulta|m}}, {{W-|lv|šautra|m}}, {{W-|lv|strēle|m}}
* {{Z|lo}}: {{W-|lo|ລູກສອນ}}, {{W-|lo|ສອນ}}
* {{Z|ln}}: {{W-|ln|likulá}}
* {{Z|lt}}: {{W-|lt|strėlė|m}}
* {{Z|khb}}: {{W-|khb|ᦔᦹᧃ|sc=Talu}}, {{W-|khb|ᦐᦱᧉᦺᦙᧉ}}
* {{Z|lb}}: {{W-|lb|Fäil|n}}
* {{Z|mk}}: {{W-|mk|стрела|m|sc=Cyrl}}
* {{Z|ml}}: {{W+|ml|അമ്പ്|sc=Mlym}}
* {{Z|ms}}: {{W+|ms|panah}}
*: {{Z|ms}}: {{W-|ms|ڤانه|sc=Arab}}
* {{Z|mt}}: {{W-|mt|vleġġa|m}}
* {{Z|mnc}}: {{W-|mnc|ᠰᡳᡵᡩᠠᠨ}}, {{W-|mnc|ᠨᡳᡵᡠ}}
* {{Z|mns-nor}}: {{W-|mns-nor|ня̄л}}
* {{Z|mi}}: {{W-|mi|kōpere}}, {{W-|mi|pere}}
* {{Z|mr}}: {{W-|mr|बाण}}
* Marî:
*: {{Z|mhr}}: {{W-|mhr|пикш}}
* {{Z|hu}}: {{W+|hu|nyíl}}
* {{Z|egy}}: {{W-egy|šsr|n|h=V6-s-r:T11}}, {{W-egy|zwn|n|h=z-wn:n-T11}}
* {{Z|moh}}: {{W-|moh|kayonkwere}}
* {{Z|mdf}}: {{W-|mdf|нал}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|сум}}
*: Mongolî: {{W-|mn|ᠰᠤᠮᠤ}}
* {{Z|mnw}}: {{W-|mnw|လဴ}}
* {{Z|nci}}: {{W-|nci|mitl}}
* {{Z|nah}}: {{W+|nah|mitl}}
* {{Z|nnt}}: {{W-|nnt|allontz}}
* {{Z|nv}}: {{W-|nv|kʼaaʼ}}
* {{Z|nds}}: {{W-|nds|Piel|n}}, {{W-|nds|Piil|n}}
* {{Z|non}}: {{W-|non|ǫr|m}}
* {{Z|no}}: {{W+|no|pil}}
*: {{Z|nb}}: {{W+|nb|pil|n|m}}
*: {{Z|nn}}: {{W-|nn|pil|m}}
* {{Z|nwy}}: {{W-|nwy|aruntquaserank}}
* {{Z|oj}}: {{W-|oj|mitigwanwi}}, {{W-|oj|bikwak}}, {{W-|oj|ginwaakwanwi}}, {{W-|oj|gii'anwaakonwi}}, {{W-|oj|asawaan}}
* {{Z|oc}}: {{W+|oc|flècha|m}}, {{W+|oc|sageta|m}}
* {{Z|one}}: {{W-|one|kayú:kwile̲’}}
* {{Z|or}}: {{W+|or|ତୀର}}
* {{Z|os}}: {{W-|os|фат|sc=Cyrl}}
* {{Z|ota}}: {{W-|ota|اوق|tr=ok}}, {{W-|ota|تیر|tr=tir}}, {{W-|ota|سهم|tr=sehm}}
* {{Z|ug}}: {{W-|ug|ئوق|sc=ug-Arab}}
* {{Z|uz}}: {{W+|uz|oʻq}}, {{W+|uz|tir}}
* {{Z|pi}}: {{W-|pi|usu}}, {{W-|pi|sara|n}}, {{W-|pi|bāṇa|n}}
* {{Z|ps}}: {{W-|ps|غشۍ|tr=ǧašëy|sc=ps-Arab}}
* {{Z|myp}}: {{W-|myp|kahaí}}
* {{Z|pdt}}: {{W-|pdt|Feil|m}}
* {{Z|pl}}: {{W+|pl|strzała|m}}, {{W-|pl|szyp|n}}
* {{Z|roa-opt}}: {{W-|roa-opt|saeta}}
* {{Z|pt}}: {{W+|pt|flecha|m}}, {{W+|pt|seta|m}}
* {{Z|pa}}: {{W-|pa|ਤੀਰ}}
* {{Z|kk}}: {{W-|kk|жебе|sc=Cyrl}}, {{W-|kk|оқ|sc=Cyrl}}
* {{Z|mwr}}: {{W-|mwr|तीर|tr=tīr}}
* {{Z|rhg}}: {{W-|rhg|tir}}
* {{Z|rm}}: {{W-|rm|frizza|m}}
* {{Z|ro}}: {{W+|ro|săgeată|m}}
*: Kirîlî: {{W-|ro|сэӂатэ|m}}
* {{Z|ru}}: {{W+|ru|стрела́|m}}
* {{Z|se}}: {{W-|se|njuolla}}
* {{Z|sa}}: {{W+|sa|इषु|n}}, {{W+|sa|बाण}}, {{W+|sa|शर|n}}
* {{Z|scn}}: {{W-|scn|freccia|m}}
* {{Z|sd}}: {{W+|sd|پيلو|tr=pīlō|cuda=پيِلو|sc=sd-Arab}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|стрела|m}}, {{W-|sh|стријела|m}}, {{W-|sh|стрелица|m}}, {{W-|sh|стрјелица|m}}, {{W-|sh|стрила|m}}
*: Latînî: {{W-|sh|strela|m}}, {{W+|sh|strijela|m}}, {{W+|sh|strelica|m}}, {{W-|sh|strjelica|m}}, {{W-|sh|strila|m}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|стрѣла|m|sc=Cyrs}}
*: Glagolîtîk: {{W-|cu|ⱄⱅⱃⱑⰾⰰ|m|sc=Glag}}
* {{Z|orv}}: {{W-|orv|стрѣла|m}}
* {{Z|sk}}: {{W-|sk|šíp}}, {{W-|sk|strela|m}}
* {{Z|sl}}: {{W+|sl|puščica|m}}
* {{Z|zlw-slv}}: {{W-|zlw-slv|střåula|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|klek|n}}, {{W-|dsb|šypa|m}}
*: {{Z|hsb}}: {{W-|hsb|kłok|n}}, {{W-|hsb|šip|n}}
* {{Z|st}}: {{W-|st|motsu}}
* {{Z|es}}: {{W+|es|flecha|m}}, {{W+|es|saeta|m}}
* {{Z|xsv}}: {{W-|xsv|strēla|m}}
* {{Z|sux}}: {{W-|sux|𒋾|tr=TI|sc=Xsux}}
* {{Z|sva}}: {{W-|sva|ცხვი}}
* {{Z|sw}}: {{W+|sw|mshale}}
* {{Z|sv}}: {{W+|sv|pil|g}}
* {{Z|sjw}}: {{W-|sjw|la nàhl we}}
* {{Z|xsr}}: {{W-|xsr|མདའ|tr=da}}
* {{Z|tab}}: {{W-|tab|чӏимир}}
* {{Z|tg}}: {{W+|tg|тир|sc=Cyrl}}
* {{Z|tl}}: {{W+|tl|tunod}}, {{W-|tl|palaso}}, {{W-|tl|pletsa}}
* {{Z|ta}}: {{W+|ta|அம்பு}}
* {{Z|twf}}: {{W-|twf|łúonemą}}
* {{Z|ttt}}: {{W-|ttt|ух}}
* {{Z|th}}: {{W-|th|ลูกศร}}, {{W-|th|ศร}}
* {{Z|te}}: {{W+|te|బాణము}}, {{W+|te|అమ్ము}}
* {{Z|crh}}: {{W-|crh|oq}}
* {{Z|tt}}: {{W+|tt|ук}}
* {{Z|tet}}: {{W-|tet|hana}}
* {{Z|ti}}: {{W-|ti|መንትግ|sc=Ethi}}, {{W-|ti|ኵናት|sc=Ethi}}, {{W-|ti|ፍላጻ|sc=Ethi}}
* {{Z|tr}}: {{W+|tr|ok}}
* {{Z|tk}}: {{W-|tk|ok}}, {{W-|tk|peýkam}}
* {{Z|bo}}: {{W-|bo|མདའ|sc=Tibt}}
* {{Z|tli}}: {{W-|tli|chooneit}}
* {{Z|txb}}: {{W-|txb|prere}}, {{W-|txb|tsain}}
* {{Z|tta}}: {{W-|tta|mañkoi}}, {{W-|tta|mañksii}}
* {{Z|tyv}}: {{W-|tyv|согун}}
* {{Z|udm}}: {{W-|udm|ньӧл}}
* {{Z|uga}}: {{W-|uga|𐎈𐎑}}
* {{Z|ur}}: {{W-|ur|تیر|n|tr=tīr|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|стріла́|m|sc=Cyrl}}
* {{Z|vi}}: {{W+|vi|mũi tên}}, {{W+|vi|tên}}
* {{Z|vo}}: {{W+|vo|sagit}}
* {{Z|wa}}: {{W+|wa|fritche|m}}, {{W+|wa|fletche|m}}
* {{Z|cy}}: {{W+|cy|saeth|m}}
* {{Z|wo}}: {{W+|wo|fitt}}, {{W-|wo|fett}}
* {{Z|km}}: {{W+|km|ព្រួញ}}, {{W+|km|សរ}}
* {{Z|xh}}: {{W-|xh|utolo}}
* {{Z|sah}}: {{W-|sah|ох|sc=Cyrl}}, {{W-|sah|оноҕос|sc=Cyrl}}
* {{Z|yi}}: {{W-|yi|פֿײַל|m}}
* {{Z|yo}}: {{W-|yo|o̩fà}}
* {{Z|esu}}: {{W-|esu|qeruq}}
* {{Z|el}}: {{W+|el|βέλος|nt}}, {{W+|el|σαΐτα|m}}
*: Kevn: {{W-|grc|ἰός|n}}, {{W-|grc|τόξευμα|nt}}, {{W-|grc|ὀϊστός|n}}
* {{Z|za}}: {{W-|za|naq}}, {{W-|za|cienq}}, {{W-|za|bwn}}
* {{Z|zu}}: {{W-|zu|umcibisholo|c3|c4}}
* {{Z|nmn}}: {{W-|nmn|gǁqhàã}}
{{werger-bin}}
:{{werger çavkanî|en|arrow|beş=Ev qismê Wergerê|oldid=75822606}}
=== Navdêr 2 ===
{{navdêr|ku|z=m}}
# [[erd|Erd]] û [[zevî|zeviyên]] [[teng]] û [[dirêj]].
#: '''''Tîra''' felankeso îsal beyar maye.''
==== Etîmolojî ====
{{ji?}}
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W-|en|strip fields}}, {{W-|en|strip fields}}, {{W-|en|strip holdings}}
{{werger-bin}}
=== Rengdêr ===
{{ku-tewîn-rd|tîr}}
{{rengdêr|ku}}, {{t-ya nerm}}, {{r-ya nerm}}
# [[şile]]ya ne [[zelal]], [[şile]]ya [[bihêz]], [[şile]]ya [[xurt]] <br>[[şile]]ya [[kêm]] [[av]] û [[zêde]] tiştek din tê de heye
#* {{jêder|ku|jêgirtin=Xwîna ku jê dihat [[sorereş]] û '''tîr''' bû.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
#* {{jêder|ku|jêgirtin=(...) carina jî bi hemû dengan ko hungiva '''tîr''' û xwîna ron tevî hev dikirin.|{{Jêgirtin/Mehmed Uzun/Hawara Dîcleyê 1/2003}}}}
# [[sift]]; tiştên [[req]], [[hişk]]; dijberê [[ronatî]]
#* {{jêder|ku|jêgirtin=(...) di devê deriyê odê de dûmaneke '''tîr''' û tûj li ruyê wî dikeve (...)|{{Jêgirtin/Hesenê Metê/Labîrenta Cinan/1994}}}}
# [[xestî]]. Mirovên bi nexweşiya şekir ketine, xestiya glukozê di nav xwîna wan de bilind e, ji ber wê yekê xwîna wan piçek tîr e.
#* {{bêmînak|ku}}
==== Dijmane ====
* {{g|ku|ruhn}}
==== Bide ber ====
{{kol3|ku
|dijwar
|sûr
|tirş
|tûj
}}
==== Jê ====
{{kol3|ku|cure=Jê
|tîr bûn<pos:lêker>
|tîr kirin<pos:lêker>
|tîrbûn<pos:navdêr>
|tîrkirin<pos:navdêr>
|tîrbûyî<pos:rengdêr>
|tîrkirî<pos:rengdêr>
|tîrahî
|tîratî
|tîrayî
|tîrî
|tîrîtî
|tîrtî
}}
==== Etîmolojî ====
Ji {{ji|ine-pro|ku|*tegus}}, [[hevreh]]a {{hv|ps|ټينګ|tr=ṭing}}, ermenî [[թանձր]] (tencir), albanî ''[[thuk]]'', îngilîzî ''[[thick]]'', almanî ''[[dick]]'', holendî ''[[dik]]''...
==== Werger ====
{{werger-ser}}
* {{Z|ady}}: {{W-|ady|ӏужъу}}
* {{Z|de}}: {{W+|de|dick}}
* {{Z|az}}: {{W-|az|qəliz}}, {{W-|az|qatı}}
* {{Z|ba}}: {{W-|ba|ҡуйы}}
* {{Z|be}}: {{W+|be|густы́}}
* {{Z|bg}}: {{W+|bg|гъст|n}}
* {{Z|my}}: {{W+|my|ပျစ်}}
* {{Z|cs}}: {{W+|cs|hustý|n}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|濃|tr=nung4}}, {{W-|yue|稠|tr=cau4}}, {{W-|yue|滐|tr=git6}}
*: {{Z|cmn}}: {{W+|cmn|濃|tr=nóng}}, {{W+|cmn|稠|tr=chóu}}, {{W+|cmn|濃厚|tr=nónghòu}}
* {{Z|da}}: {{W+|da|tyk}}
* {{Z|id}}: {{W+|id|kental}}
* {{Z|ar}}: {{W-|ar|غَلِيظ}}
* {{Z|hy}}: {{W+|hy|թանձր}}
* {{Z|fa}}: {{W+|fa|غلیظ|tr=ğaliz}}, {{W+|fa|سفت|tr=seft}}, {{W+|fa|ستبر|tr=setabr}}, {{W+|fa|دفزک|tr=dafzak}}
* {{Z|fi}}: {{W+|fi|sakea}}, {{W+|fi|tiheä}}
* {{Z|fr}}: {{W+|fr|épais}}
* {{Z|gd}}: {{W-|gd|tiugh}}
* {{Z|gl}}: {{W+|gl|testo|n}}, {{W+|gl|espeso|n}}
* {{Z|ka}}: {{W-|ka|სქელი}}
* {{Z|ht}}: {{W-|ht|pwès}}
* {{Z|nl}}: {{W+|nl|dik}}, {{W+|nl|dikke}}
* {{Z|en}}: {{W+|en|thick}} (having viscous consistency), {{W+|en|dense}}
* {{Z|ja}}: {{W+|ja|濃い|sc=Jpan|tr=こい, koi}}
* {{Z|kbd}}: {{W-|kbd|ӏувы}}
* {{Z|ca}}: {{W+|ca|espès}}
* {{Z|ko}}: {{W+|ko|짙다}}, {{W+|ko|농후하다}}
* {{Z|mk}}: {{W-|mk|густ|n}}
* {{Z|ms}}: {{W+|ms|pekat}}
* {{Z|mnc}}: {{W-|mnc|ᡨᡠᠮᡳᠨ}}
* {{Z|hu}}: {{W+|hu|sűrű}}
* {{Z|nrf}}: {{W-|nrf|êpais}}
* {{Z|oc}}: {{W+|oc|espés}}
* {{Z|ota}}: {{W-|ota|قویو|tr=koyu}}
* {{Z|ps}}: {{W-|ps|ټينګ|tr=ṭing}}
* {{Z|pl}}: {{W+|pl|gęsty}}
* {{Z|pt}}: {{W+|pt|espesso}}
* {{Z|ru}}: {{W+|ru|густо́й}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|гу̑ст}}
*: Latînî: {{W+|sh|gȗst}}
* {{Z|sk}}: {{W-|sk|hustý}}
* {{Z|sl}}: {{W+|sl|góst}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|gusty}}
* {{Z|es}}: {{W+|es|espeso}}
* {{Z|sv}}: {{W+|sv|tjock}}
* {{Z|tg}}: {{W-|tg|ғализ}}
* {{Z|th}}: {{W+|th|ข้น}}
* {{Z|te}}: {{W+|te|చిక్కని}}
* {{Z|tr}}: {{W+|tr|yoğun}}
* {{Z|uk}}: {{W+|uk|густи́й}}
* {{Z|vi}}: {{W+|vi|đặc}}
* {{Z|cy}}: {{W+|cy|tew}}
* {{Z|za}}: {{W-|za|gwd}}, {{W-|za|gwg}}, {{W-|za|gud}}
{{werger-bin}}
:{{werger çavkanî|en|thick|beş=Ev qismê Wergerê|oldid=75873085}}
sw1wk6khdajy1f8yxwbk4tgp1b1124u
6277090
6277089
2026-07-24T11:30:04Z
IbrahimKocher Duhok
4280
Werger: +îngilîzî : [[strip holdings]] (bi alîkarî)
6277090
wikitext
text/x-wiki
{{bnr-h|TIR|Tîr|tîr|tir|-tir}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{beş|Navdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr (navdêr).wav|Deng|Bidlîs}}
* {{beş|Rengdêr}} {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-tîr.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr 1 ===
{{ku-tewîn-nav|tîr|mê}}
[[Wêne:WA archery target with arrows.jpg|thumb|150px|tîr]]
{{navdêr|ku|z=m}}
# {{f|ku|çek}} [[çek|Çek]]ek [[serik]][[tûj]] e ku ji [[kevan]]ê tê [[avêtin]].
#* {{jêder|ku|jêgirtin=Eger ku awir '''tîr''' bin, çav û birû jî kevan in. Di helbestan de wekî evîndar birûyên xwe xwar bike ev mîna kevanê vedikişe, tê pejirandin. Birû ji ber ku '''tîra''' herî dijwar biaveje xwe vedikişîne.|''[[w:Di Helbestên Melayê Bateyî de Evîn û Hezkirin|Di Helbestên Melayê Bateyî de Evîn û Hezkirin]]'', [[w:Reşît Rojdar Yildiz|Reşît Rojdar Yildiz]], Bingöl Üniversitesi Yaşayan Diller Enstitüsü Dergisi}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Ji wê rojê dizanî min</br>Ko yar ew '''tîr''' li cergê da</br>Gelo dîsa bibînim ez}}
#: ''Reş e weke qîrê,</br>Rast e weke '''tîrê'''.''
==== Bi alfabeyên din ====
* {{ku-ar|تیر}}
==== Bide ber ====
* {{g|ku|rim}}
* {{g|ku|peygan}}
==== Jê ====
{{kol3|ku|cure=Jê
|sertîrik
|sertîrikî
|tîravêj
|tîravêjî
|tîrik
|tîrk
}}
==== Etîmolojî ====
Hevreha soranî {{g|ckb|تیر|tr=tîr}}, farisî {{g|fa|تیر|tr=tîr}}, tacikî [[тир]] (tîr), pehlewî ''tigr'', avestayî ''tiẍre-, tiẍri-'', ji {{ji|ira}}. Herwiha bide ber sûmerî [[𒋾]] (ti: tîr)
==== Binêre herwiha ====
{{lîste:çek/ku}}
==== Bi zaravayên din ====
* Soranî: {{W-|ckb|تیر|tr=tîr}}
==== Werger ====
{{werger-ser|tîr wek çek yan nîşan}}
* {{Z|abq}}: {{W-|abq|сагӏындакъ}}
* {{Z|ab}}: {{W-|ab|ахәымпал|sc=Cyrl}}
* {{Z|ady}}: {{W-|ady|щэбзащ}}
* {{Z|af}}: {{W-|af|pyl}}, {{W-|af|skig}}
* {{Z|ak}}: {{W-|ak|agyan}}
* {{Z|sq}}: {{W+|sq|shigjetë|m}}, {{W+|sq|ber|n}}
* {{Z|de}}: {{W+|de|Pfeil|n}}
* {{Z|amn}}: {{W-|amn|fak}}
* {{Z|am}}: {{W-|am|ፍላጣ|sc=Ethi}}, {{W-|am|ቀስት|sc=Ethi}}, {{W-|am|ፍላጻ|sc=Ethi}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|k’aa’ist'aaní}}
* {{Z|arc}}:
*: {{Z|syc}}: {{W-|syc|ܓܐܪܐ|tr=giʾrā|sc=Syrc}}
* {{Z|arw}}: {{W-|arw|simala}}
* {{Z|rup}}: {{W-|rup|sãdzeatã}}
* {{Z|as}}: {{W-|as|কাঁড়}}
* {{Z|ast}}: {{W-|ast|flecha|m}}
* {{Z|av}}: {{W-|av|йойохул гӏучӏ|sc=Cyrl}}, {{W-|av|чӏор|sc=Cyrl}}
* {{Z|ae}}: {{W-|ae|𐬙𐬌𐬔𐬭𐬌}}
* {{Z|ay}}: {{W-|ay|mich'i}}
* {{Z|az}}: {{W+|az|ox}}
*: Kirîlî: {{W-|az|ох|sc=Cyrl}}
*: {{Z|az}}: {{W-|az|اوخ|tr=ox|sc=fa-Arab}}
* {{Z|eu}}: {{W+|eu|gezi}}
* {{Z|ba}}: {{W-|ba|уҡ|sc=Cyrl}}
* {{Z|be}}: {{W-|be|страла́|m|sc=Cyrl}}
* {{Z|bn}}: {{W+|bn|তীর|sc=Beng}}
* {{Z|bi}}: {{W-|bi|ara}}
* {{Z|bor}}: {{W-|bor|tugo}}
* {{Z|bg}}: {{W+|bg|стрела́|m|sc=Cyrl}}
* {{Z|my}}: {{W+|my|မြား|sc=Mymr}}
* {{Z|ccc}}: {{W-|ccc|ashkololi}}
* {{Z|ce}}: {{W-|ce|пха}}
* {{Z|cs}}: {{W+|cs|šíp|n}}, {{W+|cs|střela|m}}
* {{Z|chr}}: {{W-|chr|ᎫᏂ}}
* {{Z|chy}}: {{W-|chy|maahe}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|箭|tr=zin3}}, {{W-|yue|矢|tr=ci2}}
*: {{Z|dng}}: {{W-|dng|җян}}
*: {{Z|cmn}}: {{W+|cmn|箭|tr=jiàn}}, {{W+|cmn|矢|tr=shǐ}}, {{W+|cmn|箭頭|tr=jiàntóu}}
*: {{Z|nan}}: {{W-|nan|箭|tr=chìⁿ}}, {{W-|nan|矢|tr=sí}}
*: {{Z|wuu}}: {{W-|wuu|箭}}, {{W-|wuu|矢}}
* {{Z|chp}}: {{W-|chp|k’á}}
* {{Z|cho}}: {{W-|cho|oo-ske-noc-kay}}
* {{Z|cv}}: {{W-|cv|ҫӗмрен|sc=Cyrl}}
* {{Z|da}}: {{W-|da|pil|g}}
* {{Z|dar}}: {{W-|dar|урги}}
* {{Z|dv}}: {{W-|dv|ތީރު|sc=Thaa}}
* {{Z|sce}}: {{W-|sce|sumu}}
* {{Z|ovd}}: {{W-|ovd|kuov|n}}
* {{Z|id}}: {{W+|id|panah}}
* {{Z|ar}}: {{W+|ar|سَهْم|n}}
*: {{Z|arz}}: {{W-|arz|سهم|n|tr=sahm}}
*: {{Z|acw}}: {{W-|acw|سَهَم|n|tr=saham}}
* {{Z|hy}}: {{W+|hy|նետ}}
* {{Z|myv}}: {{W-|myv|нал}}, {{W-|myv|нал}}
* {{Z|eo}}: {{W+|eo|sago}}
* {{Z|et}}: {{W+|et|nool}}
* {{Z|evn}}: {{W-|evn|нюр}}
* {{Z|ee}}: {{W-|ee|aŋutrɔ|nt}}
* {{Z|pal}}: {{W-|pal|𐭲𐭢𐭫|ts=tigr}}
* {{Z|fa}}: {{W+|fa|تیر|tr=tir}}
* {{Z|fo}}: {{W-|fo|ørvur|n}}, {{W-|fo|pílur|n}}, {{W-|fo|ørv|m}}
* {{Z|fi}}: {{W+|fi|nuoli}}
* {{Z|fr}}: {{W+|fr|flèche|m}}
* {{Z|fur}}: {{W-|fur|frece|m}}
* {{Z|stq}}: {{W-|stq|Stroal|n}}
* {{Z|fy}}: {{W-|fy|pylk|nt}}
* {{Z|gd}}: {{W-|gd|saighead|m}}, {{W-|gd|gath|m}}
* {{Z|gl}}: {{W-|gl|frecha|m}}, {{W-|gl|frisga|m}}, {{W+|gl|seta|m}}
* {{Z|got}}: {{W-|got|𐌰𐍂𐍈𐌰𐌶𐌽𐌰|m}}
* {{Z|gu}}: {{W-|gu|બાણ|sc=Gujr}}
* {{Z|ka}}: {{W-|ka|ისარი|sc=Geor}}
* {{Z|haw}}: {{W-|haw|pua}}, {{W-|haw|pua pana}}
* {{Z|ha}}: {{W-|ha|kibiya}}
* {{Z|hi}}: {{W+|hi|तीर|n}}, {{W+|hi|बाण|sc=Deva}}
* {{Z|nl}}: {{W+|nl|pijl|n}}
* {{Z|hrx}}: {{W-|hrx|Peil|n}}
* {{Z|he}}: {{W+|he|חֵץ|n|tr=khets|sc=Hebr}}
* {{Z|io}}: {{W+|io|flecho}}
* {{Z|ig}}: {{W-|ig|arọ}}
* {{Z|ang}}: {{W-|ang|flān|n}}
* {{Z|en}}: {{W+|en|arrow}}
* {{Z|inh}}: {{W-|inh|тӏаьск}}
* {{Z|ia}}: {{W-|ia|flecha}}, {{W-|ia|sagitta}}
* {{Z|ga}}: {{W-|ga|saighead|m}}
* {{Z|zai}}: {{W-|zai|baxa}}, {{W-|zai|baza}}
* {{Z|it}}: {{W+|it|freccia|m}}
* {{Z|is}}: {{W+|is|ör|m}}
* {{Z|ja}}: {{W+|ja|矢|tr=や, ya}}
* {{Z|jra}}: {{W-|jra|ƀrom}}
* {{Z|kaw}}: {{W-|kaw|panah}}
* {{Z|jv}}: {{W+|jv|ꦥꦤꦃ}}
* {{Z|kbd}}: {{W-|kbd|шабзэ}}
* {{Z|kl}}: {{W-|kl|qarsoq}}
* {{Z|kn}}: {{W+|kn|ಅಂಬು|sc=Knda}}, {{W+|kn|ಬಾಣ|sc=Knda}}
* {{Z|krc}}: {{W-|krc|окъ|sc=Cyrl}}
* {{Z|krl}}: {{W-|krl|piili}}
* {{Z|ca}}: {{W+|ca|fletxa|m}}, {{W+|ca|sageta|m}}
* {{Z|qu}}: {{W-|qu|wach'i}}
* {{Z|ket}}: {{W-|ket|ӄам}}
* {{Z|ky}}: {{W+|ky|жебе|sc=Cyrl}}
* {{Z|ki}}: {{W-|ki|mũguĩ|c3}}
* {{Z|si}}: {{W-|si|ඊය|sc=Sinh}}
* {{Z|koi}}: {{W-|koi|ньӧв}}
* {{Z|ko}}: {{W+|ko|화살}}
* {{Z|kw}}: {{W-|kw|seth}}
* {{Z|kum}}: {{W-|kum|окъ}}
* {{Z|lbe}}: {{W-|lbe|чӏатӏаракӏ}}
* {{Z|lkt}}: {{W-|lkt|waŋhíŋkpe}}
* {{Z|ltg}}: {{W-|ltg|šautra|m}}
* {{Z|la}}: {{W+|la|sagitta|m}}
* {{Z|lv}}: {{W-|lv|bulta|m}}, {{W-|lv|šautra|m}}, {{W-|lv|strēle|m}}
* {{Z|lo}}: {{W-|lo|ລູກສອນ}}, {{W-|lo|ສອນ}}
* {{Z|ln}}: {{W-|ln|likulá}}
* {{Z|lt}}: {{W-|lt|strėlė|m}}
* {{Z|khb}}: {{W-|khb|ᦔᦹᧃ|sc=Talu}}, {{W-|khb|ᦐᦱᧉᦺᦙᧉ}}
* {{Z|lb}}: {{W-|lb|Fäil|n}}
* {{Z|mk}}: {{W-|mk|стрела|m|sc=Cyrl}}
* {{Z|ml}}: {{W+|ml|അമ്പ്|sc=Mlym}}
* {{Z|ms}}: {{W+|ms|panah}}
*: {{Z|ms}}: {{W-|ms|ڤانه|sc=Arab}}
* {{Z|mt}}: {{W-|mt|vleġġa|m}}
* {{Z|mnc}}: {{W-|mnc|ᠰᡳᡵᡩᠠᠨ}}, {{W-|mnc|ᠨᡳᡵᡠ}}
* {{Z|mns-nor}}: {{W-|mns-nor|ня̄л}}
* {{Z|mi}}: {{W-|mi|kōpere}}, {{W-|mi|pere}}
* {{Z|mr}}: {{W-|mr|बाण}}
* Marî:
*: {{Z|mhr}}: {{W-|mhr|пикш}}
* {{Z|hu}}: {{W+|hu|nyíl}}
* {{Z|egy}}: {{W-egy|šsr|n|h=V6-s-r:T11}}, {{W-egy|zwn|n|h=z-wn:n-T11}}
* {{Z|moh}}: {{W-|moh|kayonkwere}}
* {{Z|mdf}}: {{W-|mdf|нал}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|сум}}
*: Mongolî: {{W-|mn|ᠰᠤᠮᠤ}}
* {{Z|mnw}}: {{W-|mnw|လဴ}}
* {{Z|nci}}: {{W-|nci|mitl}}
* {{Z|nah}}: {{W+|nah|mitl}}
* {{Z|nnt}}: {{W-|nnt|allontz}}
* {{Z|nv}}: {{W-|nv|kʼaaʼ}}
* {{Z|nds}}: {{W-|nds|Piel|n}}, {{W-|nds|Piil|n}}
* {{Z|non}}: {{W-|non|ǫr|m}}
* {{Z|no}}: {{W+|no|pil}}
*: {{Z|nb}}: {{W+|nb|pil|n|m}}
*: {{Z|nn}}: {{W-|nn|pil|m}}
* {{Z|nwy}}: {{W-|nwy|aruntquaserank}}
* {{Z|oj}}: {{W-|oj|mitigwanwi}}, {{W-|oj|bikwak}}, {{W-|oj|ginwaakwanwi}}, {{W-|oj|gii'anwaakonwi}}, {{W-|oj|asawaan}}
* {{Z|oc}}: {{W+|oc|flècha|m}}, {{W+|oc|sageta|m}}
* {{Z|one}}: {{W-|one|kayú:kwile̲’}}
* {{Z|or}}: {{W+|or|ତୀର}}
* {{Z|os}}: {{W-|os|фат|sc=Cyrl}}
* {{Z|ota}}: {{W-|ota|اوق|tr=ok}}, {{W-|ota|تیر|tr=tir}}, {{W-|ota|سهم|tr=sehm}}
* {{Z|ug}}: {{W-|ug|ئوق|sc=ug-Arab}}
* {{Z|uz}}: {{W+|uz|oʻq}}, {{W+|uz|tir}}
* {{Z|pi}}: {{W-|pi|usu}}, {{W-|pi|sara|n}}, {{W-|pi|bāṇa|n}}
* {{Z|ps}}: {{W-|ps|غشۍ|tr=ǧašëy|sc=ps-Arab}}
* {{Z|myp}}: {{W-|myp|kahaí}}
* {{Z|pdt}}: {{W-|pdt|Feil|m}}
* {{Z|pl}}: {{W+|pl|strzała|m}}, {{W-|pl|szyp|n}}
* {{Z|roa-opt}}: {{W-|roa-opt|saeta}}
* {{Z|pt}}: {{W+|pt|flecha|m}}, {{W+|pt|seta|m}}
* {{Z|pa}}: {{W-|pa|ਤੀਰ}}
* {{Z|kk}}: {{W-|kk|жебе|sc=Cyrl}}, {{W-|kk|оқ|sc=Cyrl}}
* {{Z|mwr}}: {{W-|mwr|तीर|tr=tīr}}
* {{Z|rhg}}: {{W-|rhg|tir}}
* {{Z|rm}}: {{W-|rm|frizza|m}}
* {{Z|ro}}: {{W+|ro|săgeată|m}}
*: Kirîlî: {{W-|ro|сэӂатэ|m}}
* {{Z|ru}}: {{W+|ru|стрела́|m}}
* {{Z|se}}: {{W-|se|njuolla}}
* {{Z|sa}}: {{W+|sa|इषु|n}}, {{W+|sa|बाण}}, {{W+|sa|शर|n}}
* {{Z|scn}}: {{W-|scn|freccia|m}}
* {{Z|sd}}: {{W+|sd|پيلو|tr=pīlō|cuda=پيِلو|sc=sd-Arab}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|стрела|m}}, {{W-|sh|стријела|m}}, {{W-|sh|стрелица|m}}, {{W-|sh|стрјелица|m}}, {{W-|sh|стрила|m}}
*: Latînî: {{W-|sh|strela|m}}, {{W+|sh|strijela|m}}, {{W+|sh|strelica|m}}, {{W-|sh|strjelica|m}}, {{W-|sh|strila|m}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|стрѣла|m|sc=Cyrs}}
*: Glagolîtîk: {{W-|cu|ⱄⱅⱃⱑⰾⰰ|m|sc=Glag}}
* {{Z|orv}}: {{W-|orv|стрѣла|m}}
* {{Z|sk}}: {{W-|sk|šíp}}, {{W-|sk|strela|m}}
* {{Z|sl}}: {{W+|sl|puščica|m}}
* {{Z|zlw-slv}}: {{W-|zlw-slv|střåula|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|klek|n}}, {{W-|dsb|šypa|m}}
*: {{Z|hsb}}: {{W-|hsb|kłok|n}}, {{W-|hsb|šip|n}}
* {{Z|st}}: {{W-|st|motsu}}
* {{Z|es}}: {{W+|es|flecha|m}}, {{W+|es|saeta|m}}
* {{Z|xsv}}: {{W-|xsv|strēla|m}}
* {{Z|sux}}: {{W-|sux|𒋾|tr=TI|sc=Xsux}}
* {{Z|sva}}: {{W-|sva|ცხვი}}
* {{Z|sw}}: {{W+|sw|mshale}}
* {{Z|sv}}: {{W+|sv|pil|g}}
* {{Z|sjw}}: {{W-|sjw|la nàhl we}}
* {{Z|xsr}}: {{W-|xsr|མདའ|tr=da}}
* {{Z|tab}}: {{W-|tab|чӏимир}}
* {{Z|tg}}: {{W+|tg|тир|sc=Cyrl}}
* {{Z|tl}}: {{W+|tl|tunod}}, {{W-|tl|palaso}}, {{W-|tl|pletsa}}
* {{Z|ta}}: {{W+|ta|அம்பு}}
* {{Z|twf}}: {{W-|twf|łúonemą}}
* {{Z|ttt}}: {{W-|ttt|ух}}
* {{Z|th}}: {{W-|th|ลูกศร}}, {{W-|th|ศร}}
* {{Z|te}}: {{W+|te|బాణము}}, {{W+|te|అమ్ము}}
* {{Z|crh}}: {{W-|crh|oq}}
* {{Z|tt}}: {{W+|tt|ук}}
* {{Z|tet}}: {{W-|tet|hana}}
* {{Z|ti}}: {{W-|ti|መንትግ|sc=Ethi}}, {{W-|ti|ኵናት|sc=Ethi}}, {{W-|ti|ፍላጻ|sc=Ethi}}
* {{Z|tr}}: {{W+|tr|ok}}
* {{Z|tk}}: {{W-|tk|ok}}, {{W-|tk|peýkam}}
* {{Z|bo}}: {{W-|bo|མདའ|sc=Tibt}}
* {{Z|tli}}: {{W-|tli|chooneit}}
* {{Z|txb}}: {{W-|txb|prere}}, {{W-|txb|tsain}}
* {{Z|tta}}: {{W-|tta|mañkoi}}, {{W-|tta|mañksii}}
* {{Z|tyv}}: {{W-|tyv|согун}}
* {{Z|udm}}: {{W-|udm|ньӧл}}
* {{Z|uga}}: {{W-|uga|𐎈𐎑}}
* {{Z|ur}}: {{W-|ur|تیر|n|tr=tīr|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|стріла́|m|sc=Cyrl}}
* {{Z|vi}}: {{W+|vi|mũi tên}}, {{W+|vi|tên}}
* {{Z|vo}}: {{W+|vo|sagit}}
* {{Z|wa}}: {{W+|wa|fritche|m}}, {{W+|wa|fletche|m}}
* {{Z|cy}}: {{W+|cy|saeth|m}}
* {{Z|wo}}: {{W+|wo|fitt}}, {{W-|wo|fett}}
* {{Z|km}}: {{W+|km|ព្រួញ}}, {{W+|km|សរ}}
* {{Z|xh}}: {{W-|xh|utolo}}
* {{Z|sah}}: {{W-|sah|ох|sc=Cyrl}}, {{W-|sah|оноҕос|sc=Cyrl}}
* {{Z|yi}}: {{W-|yi|פֿײַל|m}}
* {{Z|yo}}: {{W-|yo|o̩fà}}
* {{Z|esu}}: {{W-|esu|qeruq}}
* {{Z|el}}: {{W+|el|βέλος|nt}}, {{W+|el|σαΐτα|m}}
*: Kevn: {{W-|grc|ἰός|n}}, {{W-|grc|τόξευμα|nt}}, {{W-|grc|ὀϊστός|n}}
* {{Z|za}}: {{W-|za|naq}}, {{W-|za|cienq}}, {{W-|za|bwn}}
* {{Z|zu}}: {{W-|zu|umcibisholo|c3|c4}}
* {{Z|nmn}}: {{W-|nmn|gǁqhàã}}
{{werger-bin}}
:{{werger çavkanî|en|arrow|beş=Ev qismê Wergerê|oldid=75822606}}
=== Navdêr 2 ===
{{navdêr|ku|z=m}}
# [[erd|Erd]] û [[zevî|zeviyên]] [[teng]] û [[dirêj]].
#: '''''Tîra''' felankeso îsal beyar maye.''
==== Etîmolojî ====
{{ji?}}
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W-|en|strip fields}}, {{W-|en|strip fields}}, {{W-|en|strip holdings}}, {{W-|en|strip holdings}}
{{werger-bin}}
=== Rengdêr ===
{{ku-tewîn-rd|tîr}}
{{rengdêr|ku}}, {{t-ya nerm}}, {{r-ya nerm}}
# [[şile]]ya ne [[zelal]], [[şile]]ya [[bihêz]], [[şile]]ya [[xurt]] <br>[[şile]]ya [[kêm]] [[av]] û [[zêde]] tiştek din tê de heye
#* {{jêder|ku|jêgirtin=Xwîna ku jê dihat [[sorereş]] û '''tîr''' bû.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
#* {{jêder|ku|jêgirtin=(...) carina jî bi hemû dengan ko hungiva '''tîr''' û xwîna ron tevî hev dikirin.|{{Jêgirtin/Mehmed Uzun/Hawara Dîcleyê 1/2003}}}}
# [[sift]]; tiştên [[req]], [[hişk]]; dijberê [[ronatî]]
#* {{jêder|ku|jêgirtin=(...) di devê deriyê odê de dûmaneke '''tîr''' û tûj li ruyê wî dikeve (...)|{{Jêgirtin/Hesenê Metê/Labîrenta Cinan/1994}}}}
# [[xestî]]. Mirovên bi nexweşiya şekir ketine, xestiya glukozê di nav xwîna wan de bilind e, ji ber wê yekê xwîna wan piçek tîr e.
#* {{bêmînak|ku}}
==== Dijmane ====
* {{g|ku|ruhn}}
==== Bide ber ====
{{kol3|ku
|dijwar
|sûr
|tirş
|tûj
}}
==== Jê ====
{{kol3|ku|cure=Jê
|tîr bûn<pos:lêker>
|tîr kirin<pos:lêker>
|tîrbûn<pos:navdêr>
|tîrkirin<pos:navdêr>
|tîrbûyî<pos:rengdêr>
|tîrkirî<pos:rengdêr>
|tîrahî
|tîratî
|tîrayî
|tîrî
|tîrîtî
|tîrtî
}}
==== Etîmolojî ====
Ji {{ji|ine-pro|ku|*tegus}}, [[hevreh]]a {{hv|ps|ټينګ|tr=ṭing}}, ermenî [[թանձր]] (tencir), albanî ''[[thuk]]'', îngilîzî ''[[thick]]'', almanî ''[[dick]]'', holendî ''[[dik]]''...
==== Werger ====
{{werger-ser}}
* {{Z|ady}}: {{W-|ady|ӏужъу}}
* {{Z|de}}: {{W+|de|dick}}
* {{Z|az}}: {{W-|az|qəliz}}, {{W-|az|qatı}}
* {{Z|ba}}: {{W-|ba|ҡуйы}}
* {{Z|be}}: {{W+|be|густы́}}
* {{Z|bg}}: {{W+|bg|гъст|n}}
* {{Z|my}}: {{W+|my|ပျစ်}}
* {{Z|cs}}: {{W+|cs|hustý|n}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|濃|tr=nung4}}, {{W-|yue|稠|tr=cau4}}, {{W-|yue|滐|tr=git6}}
*: {{Z|cmn}}: {{W+|cmn|濃|tr=nóng}}, {{W+|cmn|稠|tr=chóu}}, {{W+|cmn|濃厚|tr=nónghòu}}
* {{Z|da}}: {{W+|da|tyk}}
* {{Z|id}}: {{W+|id|kental}}
* {{Z|ar}}: {{W-|ar|غَلِيظ}}
* {{Z|hy}}: {{W+|hy|թանձր}}
* {{Z|fa}}: {{W+|fa|غلیظ|tr=ğaliz}}, {{W+|fa|سفت|tr=seft}}, {{W+|fa|ستبر|tr=setabr}}, {{W+|fa|دفزک|tr=dafzak}}
* {{Z|fi}}: {{W+|fi|sakea}}, {{W+|fi|tiheä}}
* {{Z|fr}}: {{W+|fr|épais}}
* {{Z|gd}}: {{W-|gd|tiugh}}
* {{Z|gl}}: {{W+|gl|testo|n}}, {{W+|gl|espeso|n}}
* {{Z|ka}}: {{W-|ka|სქელი}}
* {{Z|ht}}: {{W-|ht|pwès}}
* {{Z|nl}}: {{W+|nl|dik}}, {{W+|nl|dikke}}
* {{Z|en}}: {{W+|en|thick}} (having viscous consistency), {{W+|en|dense}}
* {{Z|ja}}: {{W+|ja|濃い|sc=Jpan|tr=こい, koi}}
* {{Z|kbd}}: {{W-|kbd|ӏувы}}
* {{Z|ca}}: {{W+|ca|espès}}
* {{Z|ko}}: {{W+|ko|짙다}}, {{W+|ko|농후하다}}
* {{Z|mk}}: {{W-|mk|густ|n}}
* {{Z|ms}}: {{W+|ms|pekat}}
* {{Z|mnc}}: {{W-|mnc|ᡨᡠᠮᡳᠨ}}
* {{Z|hu}}: {{W+|hu|sűrű}}
* {{Z|nrf}}: {{W-|nrf|êpais}}
* {{Z|oc}}: {{W+|oc|espés}}
* {{Z|ota}}: {{W-|ota|قویو|tr=koyu}}
* {{Z|ps}}: {{W-|ps|ټينګ|tr=ṭing}}
* {{Z|pl}}: {{W+|pl|gęsty}}
* {{Z|pt}}: {{W+|pt|espesso}}
* {{Z|ru}}: {{W+|ru|густо́й}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|гу̑ст}}
*: Latînî: {{W+|sh|gȗst}}
* {{Z|sk}}: {{W-|sk|hustý}}
* {{Z|sl}}: {{W+|sl|góst}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|gusty}}
* {{Z|es}}: {{W+|es|espeso}}
* {{Z|sv}}: {{W+|sv|tjock}}
* {{Z|tg}}: {{W-|tg|ғализ}}
* {{Z|th}}: {{W+|th|ข้น}}
* {{Z|te}}: {{W+|te|చిక్కని}}
* {{Z|tr}}: {{W+|tr|yoğun}}
* {{Z|uk}}: {{W+|uk|густи́й}}
* {{Z|vi}}: {{W+|vi|đặc}}
* {{Z|cy}}: {{W+|cy|tew}}
* {{Z|za}}: {{W-|za|gwd}}, {{W-|za|gwg}}, {{W-|za|gud}}
{{werger-bin}}
:{{werger çavkanî|en|thick|beş=Ev qismê Wergerê|oldid=75873085}}
7wyb1av7ufh69f0fy9m54qiyd9jdhow
bax
0
18434
6276943
5996806
2026-07-23T17:22:15Z
Wikihez
7344
/* Jê */
6276943
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{IPA|ku|/ˈbɑːx/}} (bax) an jî {{IPAchar|/ˈbɑːɣ/}} ([[baẍ]])
* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-bax.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr 1 ===
{{ku-tewîn-nav|bax|nêr}}
[[Wêne:SF Japanese Garden.JPG|thumb|250px|Bax (1)]]
{{navdêr|ku|z=n}}
# [[baxçe|Baxçe]], [[gulistan]], [[parêz]], <br>[[cih]]ê ku [[gul]] yan [[dar]] lê hatine [[çandin]].
#* {{jêder|ku|{{Jêgirtin/Mela Hisênê Bateyî/Mewlûda Kurdî}}|jêgirtin=Daniye pişta wî ew Nûra 'Ezîm</br>Paşê bir avête '''baxê''' Cennetê}}
==== Bi alfabeyên din ====
* {{ku-ar|باغ}}, {{g|ku|sc=Arab|باخ}}
==== Jê ====
{{kol3|ku|cure=Jê
|baxçe
|baxçevan
|baxçevanî
|baxçevanîtî
|baxçevantî
|baxçeyî
|baxewan
|baxvan
|baxî
|baxvanî
|baxvanîtî
|baxvantî
}}
==== Etîmolojî ====
Hevreha {{hv|fa|باغ|tr=baẍ|w=bax, baxçe}}, belûçî {{g|bal|باگ|tr=bag|w=bax, baxçe}}, pehlewî ''bag'' (baxçe; milk, erd, zevî; maliyet; qismet), [[hevreh]]a [[bexş]], [[bext]], [[boq]], [[helboq]], [[baq]], [[pag]], [[xerboq]], [[bac]], hemû ji {{ji|ine-pro|ku|-}} [[bʰeh₂g-]] ([[belav]] kirin, [[parve]] kirin). Wek [[bağ]] û [[bahçe]] (ji [[baxçe]]) ketiye tirkî jî û wek [[ܒܓܐ]] (bağa) jî di aramî de cih girtiye.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|tuin}}
* {{Z|sq}}: {{W+|sq|kopsht}}
* {{Z|de}}: {{W+|de|Garten|m}}, {{W+|de|Anlage|f}}, {{W+|de|Feld|m}}, {{W+|de|Strauß|m}}, {{W+|de|Weinberg|m}}, {{W+|de|Weingarten|m}}, {{W+|de|Prise|f|guman=erê}}
* {{Z|br}}: {{W+|br|liorzh}}
* {{Z|cs}}: {{W+|cs|zahrada}}
* {{Z|da}}: {{W+|da|have}}
* {{Z|id}}: {{W+|id|kebun}}
* {{Z|ar}}: {{W+|ar|حديقة|tr=ḥedîqe<sup>t</sup>}}, {{W-|ar|الربیعة|guman=erê}}, {{W-|ar|الرقمة|guman=erê}}, {{W-|ar|الروض|guman=erê}}, {{W-|ar|الروضة|guman=erê}}, {{W-|ar|الطنء|guman=erê}}, {{W-|ar|المألة|guman=erê}}, {{W-|ar|المرغ|guman=erê}}, {{W-|ar|الناعمة|guman=erê}}, {{W-|ar|أیك|guman=erê}}, {{W-|ar|مرغة|guman=erê}}
* {{Z|eo}}: {{W+|eo|ĝardeno}}
* {{Z|fa}}: {{W+|fa|باغ|tr=baẍ}}, {{W+|fa|باغ}}, {{W+|fa|باغ میوه}}
* {{Z|fo}}: {{W-|fo|urtagarður}}
* {{Z|fi}}: {{W+|fi|puutarha}}
* {{Z|fr}}: {{W+|fr|jardin}}
* {{Z|fy}}: {{W-|fy|tún}}
* {{Z|gd}}: {{W-|gd|gàradh}}
* {{Z|gl}}: {{W+|gl|xardín|m}}
* {{Z|nl}}: {{W+|nl|hof}}, {{W+|nl|tuin}}, {{W+|nl|gaard}}, {{W+|nl|gaarde}}
* {{Z|he}}: {{W+|he|גן|cuda=גַּן}} {{mf}}
* {{Z|io}}: {{W+|io|gardeno}}
* {{Z|en}}: {{W+|en|orchard}}
* {{Z|it}}: {{W+|it|giardino}}
* {{Z|is}}: {{W+|is|garður}}
* {{Z|ja}}: {{W+|ja|庭|tr=にわ, niwa}}
* {{Z|ca}}: {{W+|ca|jardí}}
* {{Z|kw}}: {{W-|kw|lowarth}}
* {{Z|la}}: {{W+|la|hortus}}
* {{Z|li}}: {{W-|li|tuin}}
* {{Z|ms}}: {{W+|ms|kebun}}
* {{Z|hu}}: {{W+|hu|kert}}
* {{Z|no}}: {{W+|no|have}}
* {{Z|pap}}: {{W-|pap|hardin}}, {{W-|pap|hòfi}}
* {{Z|pl}}: {{W+|pl|ogród}}
* {{Z|pt}}: {{W+|pt|horto}}, {{W+|pt|jardim}}
* {{Z|ro}}: {{W+|ro|grădină}}
* {{Z|ru}}: {{W+|ru|сад}}
* {{Z|se}}: {{W-|se|šaddogárdi}}
* {{Z|srn}}: {{W-|srn|dyari}}
* {{Z|ses}}: {{W-|ses|janbu}}
* {{Z|es}}: {{W+|es|jardín}}
* {{Z|sw}}: {{W+|sw|bustani}}
* {{Z|sv}}: {{W+|sv|trädgård}}
* {{Z|tl}}: {{W+|tl|halamánan}}, {{W+|tl|hardín}}
* {{Z|tr}}: {{W+|tr|bahçe}}, {{W+|tr|bağ}}, {{W-|tr|demet tel}}, {{W+|tr|tutam}}
* {{Z|cy}}: {{W+|cy|gardd}}
* {{Z|el}}: {{W+|el|κήπος}}, {{W+|el|περιβόλι}}
* {{Z|zu}}: {{W+|zu|ingadi}}
{{werger-bin}}
=== Navdêr 2 ===
{{navdêr|ku|z=n}}
# ''Binere'': '''[[baq]]'''
7vufwip5jn1wdw5iipfif3s75qpa42y
6276944
6276943
2026-07-23T17:23:59Z
Wikihez
7344
/* Navdêr 1 */ [[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6276944
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{IPA|ku|/ˈbɑːx/}} (bax) an jî {{IPAchar|/ˈbɑːɣ/}} ([[baẍ]])
* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-bax.wav|Deng|Bidlîs}}
* {{ku-kîte}}
=== Navdêr 1 ===
{{ku-tewîn-nav|bax|nêr}}
[[Wêne:SF Japanese Garden.JPG|thumb|250px|Bax (1)]]
{{navdêr|ku|z=n}}
# [[cih|Cih]]ê ku [[gul]] yan [[dar]] lê hatine [[çandin]]
#: {{hevmane|ku|baxçe|Baxçe|gulistan|parêz}}
#* {{jêder|ku|{{Jêgirtin/Mela Hisênê Bateyî/Mewlûda Kurdî}}|jêgirtin=Daniye pişta wî ew Nûra 'Ezîm</br>Paşê bir avête '''baxê''' Cennetê}}
==== Bi alfabeyên din ====
* {{ku-ar|باغ}}, {{g|ku|sc=Arab|باخ}}
==== Jê ====
{{kol3|ku|cure=Jê
|baxçe
|baxçevan
|baxçevanî
|baxçevanîtî
|baxçevantî
|baxçeyî
|baxewan
|baxvan
|baxî
|baxvanî
|baxvanîtî
|baxvantî
}}
==== Etîmolojî ====
Hevreha {{hv|fa|باغ|tr=baẍ|w=bax, baxçe}}, belûçî {{g|bal|باگ|tr=bag|w=bax, baxçe}}, {{hv|pal||bag|sc=Latn}} (baxçe; milk, erd, zevî; maliyet; qismet), hevreha [[bexş]], [[bext]], [[boq]], [[helboq]], [[baq]], [[pag]], [[xerboq]], [[bac]], hemû ji {{ji|ine-pro|ku|-}} [[bʰeh₂g-]] ([[belav]] kirin, [[parve]] kirin). Wek [[bağ]] û [[bahçe]] (ji [[baxçe]]) ketiye tirkî jî û wek [[ܒܓܐ]] (bağa) jî di aramî de cih girtiye.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|tuin}}
* {{Z|sq}}: {{W+|sq|kopsht}}
* {{Z|de}}: {{W+|de|Garten|m}}, {{W+|de|Anlage|f}}, {{W+|de|Feld|m}}, {{W+|de|Strauß|m}}, {{W+|de|Weinberg|m}}, {{W+|de|Weingarten|m}}, {{W+|de|Prise|f|guman=erê}}
* {{Z|br}}: {{W+|br|liorzh}}
* {{Z|cs}}: {{W+|cs|zahrada}}
* {{Z|da}}: {{W+|da|have}}
* {{Z|id}}: {{W+|id|kebun}}
* {{Z|ar}}: {{W+|ar|حديقة|tr=ḥedîqe<sup>t</sup>}}, {{W-|ar|الربیعة|guman=erê}}, {{W-|ar|الرقمة|guman=erê}}, {{W-|ar|الروض|guman=erê}}, {{W-|ar|الروضة|guman=erê}}, {{W-|ar|الطنء|guman=erê}}, {{W-|ar|المألة|guman=erê}}, {{W-|ar|المرغ|guman=erê}}, {{W-|ar|الناعمة|guman=erê}}, {{W-|ar|أیك|guman=erê}}, {{W-|ar|مرغة|guman=erê}}
* {{Z|eo}}: {{W+|eo|ĝardeno}}
* {{Z|fa}}: {{W+|fa|باغ|tr=baẍ}}, {{W+|fa|باغ}}, {{W+|fa|باغ میوه}}
* {{Z|fo}}: {{W-|fo|urtagarður}}
* {{Z|fi}}: {{W+|fi|puutarha}}
* {{Z|fr}}: {{W+|fr|jardin}}
* {{Z|fy}}: {{W-|fy|tún}}
* {{Z|gd}}: {{W-|gd|gàradh}}
* {{Z|gl}}: {{W+|gl|xardín|m}}
* {{Z|nl}}: {{W+|nl|hof}}, {{W+|nl|tuin}}, {{W+|nl|gaard}}, {{W+|nl|gaarde}}
* {{Z|he}}: {{W+|he|גן|cuda=גַּן}} {{mf}}
* {{Z|io}}: {{W+|io|gardeno}}
* {{Z|en}}: {{W+|en|orchard}}
* {{Z|it}}: {{W+|it|giardino}}
* {{Z|is}}: {{W+|is|garður}}
* {{Z|ja}}: {{W+|ja|庭|tr=にわ, niwa}}
* {{Z|ca}}: {{W+|ca|jardí}}
* {{Z|kw}}: {{W-|kw|lowarth}}
* {{Z|la}}: {{W+|la|hortus}}
* {{Z|li}}: {{W-|li|tuin}}
* {{Z|ms}}: {{W+|ms|kebun}}
* {{Z|hu}}: {{W+|hu|kert}}
* {{Z|no}}: {{W+|no|have}}
* {{Z|pap}}: {{W-|pap|hardin}}, {{W-|pap|hòfi}}
* {{Z|pl}}: {{W+|pl|ogród}}
* {{Z|pt}}: {{W+|pt|horto}}, {{W+|pt|jardim}}
* {{Z|ro}}: {{W+|ro|grădină}}
* {{Z|ru}}: {{W+|ru|сад}}
* {{Z|se}}: {{W-|se|šaddogárdi}}
* {{Z|srn}}: {{W-|srn|dyari}}
* {{Z|ses}}: {{W-|ses|janbu}}
* {{Z|es}}: {{W+|es|jardín}}
* {{Z|sw}}: {{W+|sw|bustani}}
* {{Z|sv}}: {{W+|sv|trädgård}}
* {{Z|tl}}: {{W+|tl|halamánan}}, {{W+|tl|hardín}}
* {{Z|tr}}: {{W+|tr|bahçe}}, {{W+|tr|bağ}}, {{W-|tr|demet tel}}, {{W+|tr|tutam}}
* {{Z|cy}}: {{W+|cy|gardd}}
* {{Z|el}}: {{W+|el|κήπος}}, {{W+|el|περιβόλι}}
* {{Z|zu}}: {{W+|zu|ingadi}}
{{werger-bin}}
=== Navdêr 2 ===
{{navdêr|ku|z=n}}
# ''Binere'': '''[[baq]]'''
61gpfixppxpzk4703r9ah074p2nxlvf
telefona berîkê
0
21356
6277065
6262908
2026-07-24T05:00:58Z
Peyvkar
14865
/* Formên alternatîv */ Form lê hate zêdekirin
6277065
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|telefona berîkê|-}}
{{navdêr|ku|z=m}}
# [[telefon|Telefon]]a ku mirov dikare li gel xwe [[hilgirtin|hilgire]] û bi xwe re bîne û bibe.
#: {{hevmane|ku|telefona destan|telefona mobîl|telefona bêtêl}}
==== Formên alternatîv ====
* {{g|ku|telefona beriyê}}
* {{g|ku|têlefona cêbê}}
==== Etîmolojî ====
Ji ''[[telefon]] + [[-a]] + [[berîk]] + [[-ê]]''.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|selfoon}}, {{W-|af|sellulêre telefoon}}
* {{Z|sq}}: {{W-|sq|telefon celular|n}}
* {{Z|de}}: {{W+|de|Mobiltelefon|nt}}, {{W+|de|Funktelefon|nt}}, {{W+|de|Handy|nt}}, {{W+|de|Natel|nt}}
* {{Z|am}}: {{W-|am|ነፋስ ስልክ}}, {{W-|am|ተንቀሳቃሽ ስልክ}}, {{W-|am|ሞባይል ስልክ}}
* {{Z|as}}: {{W-|as|ম’বাইল ফোন}}
* {{Z|az}}: {{W-|az|mobil telefon}}
* {{Z|eu}}: {{W-|eu|telefono mugikor}}, {{W-|eu|sakelako telefono}}, {{W-|eu|mobil}}
* {{Z|ba}}: {{W-|ba|кеҫә телефоны}}
* {{Z|be}}: {{W-|be|мабі́льны тэлефо́н|n}}, {{W-|be|со́тавы тэлефо́н|n}}, {{W-|be|мабі́льнік|n}}
* {{Z|bn}}: {{W-|bn|মোবাইল ফোন}}, {{W-|bn|মোবাইল}}, {{W+|bn|মুঠোফোন}}
* {{Z|bg}}: {{W-|bg|моби́лен телефо́н|n}}, {{W-|bg|кле́тъчен телефо́н|n}}, {{W-|bg|преноси́м телефо́н|n}}, {{W-|bg|мобифо́н|n}}
* {{Z|my}}: {{W-|my|မိုဘိုင်းလ်ဖုန်း}}
* {{Z|dz}}: {{W-|dz|འགྲུལ་འཕྲིན།}}, {{W-|dz|བརྒྱུད་འཕྲིན}}
* {{Z|cs}}: {{W+|cs|mobilní telefon|n}}, {{W+|cs|mobil|n}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|手提電話|tr=sau2 tai4 din6 waa6-2}}, {{W-|yue|手機|tr=sau2 gei1}}, {{W-|yue|流動電話|tr=lau4 dung6 din6 waa6-2}}, {{W-|yue|大哥大|tr=daai6 go1 daai6}}
*: {{Z|dng}}: {{W-|dng|шуҗи}}
*: {{Z|hak}}: {{W-|hak|手機仔|tr=sú-kî-é}}, {{W-|hak|行動電話|tr=hàng-thung thien-fa, hàng-thûng thien-fa}}
*: {{Z|cmn}}: {{W+|cmn|手機}}, {{W+|cmn|手提電話}}, {{W+|cmn|移動電話|tr=yídòng diànhuà}}, {{W+|cmn|行動電話}}, {{W+|cmn|大哥大}}
*: {{Z|cdo}}: {{W-|cdo|手機|tr=chiū-gĭ}}
*: {{Z|nan}}: {{W+|nan|手機仔|tr=chhiú-ki-á}}, {{W-|nan|手機|tr=chhiú-ki}}, {{W-|nan|行動電話|tr=hêng-tōng tiān-ōe}}
*: {{Z|wuu}}: {{W-|wuu|手機}}
* {{Z|da}}: {{W+|da|mobiltelefon|g}}
* {{Z|id}}: {{W-|id|telepon selular}}, {{W+|id|telepon genggam}}, {{W+|id|telepon seluler}}
* {{Z|ar}}: {{W+|ar|جَوَّال|n}}, {{W+|ar|هَاتِف مَحْمُول|n}}, {{W-|ar|هَاتِف خَلَوِيّ|n}}, {{W-|ar|هَاتِف مُتَحَرَّك|n}}, {{W-|ar|هَاتِف النَّقَّال|n}}, {{W-|ar|مُوبِيل|n}}
*: {{Z|acw}}: {{W-|acw|جَوَّال|n|tr=jawwāl}}
*: {{Z|ary}}: {{W-|ary|بورطابل|n|tr=burṭabl}}, {{W-|ary|تلفون|tr=tilifun}}
* {{Z|hy}}: {{W+|hy|բջջային հեռախոս}}, {{W+|hy|բջջային}}
* {{Z|eo}}: {{W+|eo|posxtelefono|cuda=poŝtelefono}}, {{W+|eo|poŝtelefono}}
* {{Z|et}}: {{W+|et|mobiiltelefon}}
* {{Z|fa}}: {{W+|fa|تِلِفُنِ هَمْراه|tr=telefon-e hamrâh}}, {{W-|fa|تِلِفُنِ موبایْل|tr=telefon-e mobâyl}}, {{W+|fa|موبایْل|tr=mobâyl}}, {{W-|fa|گوشیِ هَمْراه|tr=guši-ye hamrâh}}, {{W-|fa|گوشیِ موبایْل|tr=guši-ye mobâyl}}, {{W+|fa|گوشی|tr=guši}}
* {{Z|fo}}: {{W-|fo|fartelefon|m}}
* {{Z|fi}}: {{W+|fi|matkapuhelin}}, {{W+|fi|kännykkä}}, {{W+|fi|kapula}}
* {{Z|fr}}: {{W+|fr|téléphone mobile|n}}, {{W+|fr|mobile}}, {{W+|fr|téléphone portable|n}} {{W+|fr|portable|n}}, {{W+|fr|GSM|n}}, {{W+|fr|téléphone cellulaire|n}}, {{W+|fr|cellulaire|n}}, {{W+|fr|natel|n}}
* {{Z|gd}}: {{W-|gd|fòn-làimhe}}
* {{Z|gl}}: {{W+|gl|teléfono móbil|n}}, {{W+|gl|móbil|n}}
* {{Z|gu}}: {{W-|gu|મોબાઈલ|nt}}, {{W-|gu|મોબાઇલ ફોન}}
* {{Z|ka}}: {{W-|ka|მობილური}}, {{W-|ka|მობილური ტელეფონი}}
* {{Z|hi}}: {{W-|hi|मोबाइल फ़ोन|n}}, {{W+|hi|मोबाइल|n}}
* {{Z|nl}}: {{W-|nl|mobiele telefoon|n}}, {{W+|nl|gsm}}, {{W+|nl|mobieltje|nt}}
* {{Z|he}}: {{W+|he|טֶלֶפוֹן סֶלוּלָרי|n|tr=telefon selulari}}, {{W+|he|טלפון נייד|n|tr=telefon nayad|cuda=טלפון נייד / טֶלֶפוֹן נַיָּד}}
* {{Z|en}}: {{W+|en|mobile phone}}
* {{Z|ia}}: {{W-|ia|telephono mobile}}
* {{Z|ga}}: {{W-|ga|guthán soghluaiste|n}}, {{W-|ga|guthán póca|n}}, {{W-|ga|fón póca|n}}
* {{Z|it}}: {{W+|it|cellulare|n}}, {{W+|it|telefono cellulare|n}}, {{W-|it|telefono mobile|n}}, {{W+|it|telefonino|n}}, {{W+|it|natel|n}}
* {{Z|is}}: {{W+|is|farsími|n}}, {{W+|is|gemsi|n}}, {{W-|is|GSM-sími|n}}
* {{Z|ja}}: {{W+|ja|携帯電話|tr=けいたいでんわ, keitai denwa}}, {{W+|ja|携帯|tr=けいたい, keitai}}, {{W-|ja|セルホーン|tr=seruhōn}}, {{W-|ja|セルフォン|tr=serufon}}, {{W-|ja|モバイル|tr=mobairu}}
* {{Z|kl}}: {{W-|kl|mobili}}, {{W-|kl|oqarasuaat nassartariaq}}, {{W-|kl|oqarasuaat angallattagaq}}
* {{Z|xal}}: {{W-|xal|һар утцн}}
* {{Z|kn}}: {{W+|kn|ಮೊಬೈಲ್ ಫೋನ್}}
* {{Z|ca}}: {{W+|ca|telèfon mòbil|n}}, {{W+|ca|mòbil}}
* {{Z|ky}}: {{W+|ky|кол телефон}}, {{W-|ky|уюлдук телефон}}
* {{Z|si}}: {{W-|si|ජංගම දුරකථනය}}
* {{Z|ko}}: {{W+|ko|휴대전화}}, {{W+|ko|핸드폰}}, {{W+|ko|휴대폰}}, {{W+|ko|손전화}}
* {{Z|kw}}: {{W-|kw|klapkodh|m}}, {{W-|kw|pellgowsel|m}}, {{W-|kw|kellgowser|n}}
* {{Z|rop}}: {{W-|rop|telefonn portab}}, {{W-|rop|portab}}, {{W-|rop|mobayl}}
* {{Z|lad}}: {{W-|lad|djep|n}}, {{W-|lad|pélefon|n}}
* {{Z|la}}: {{W-|la|tēlephōnum gestābile|nt}}, {{W-|la|tēlephōnium gestābile|nt}}, {{W-|la|tēlephōnum portātile|nt}}, {{W-|la|tēlephōnium portātile|nt}}, {{W-|la|tēlephōnum portābile|nt}}, {{W-|la|tēlephōnium portābile|nt}}, {{W-|la|tēlephōnulum|nt}}
* {{Z|lv}}: {{W-|lv|mobilais tālrunis|n}}, {{W-|lv|mobilais telefons|n}}, {{W-|lv|mobilais|n}}
* {{Z|lo}}: {{W+|lo|ໂທລະສັບມືຖື}}
* {{Z|lt}}: {{W-|lt|mobilusis telefonas|n}}
* {{Z|mk}}: {{W-|mk|мобилен телефо́н|n}}, {{W-|mk|мобилен|n}}, {{W-|mk|преносен телефо́н|n}}
* {{Z|ml}}: {{W-|ml|മൊബൈൽഫോൺ}}, {{W-|ml|ജംഗമദൂരവാണി}}, {{W-|ml|ദൂരഭാഷണശ്രവണസഹായി}}, {{W-|ml|സഞ്ചാരദൂരഭാഷിണി}}, {{W-|ml|സെൽഫോൺ}}
* {{Z|mfa}}: {{W-|mfa|telefon tangan}}
* {{Z|ms}}: {{W+|ms|telefon bimbit}}
* {{Z|mt}}: {{W-|mt|mowbajl|n}}, {{W-|mt|telefon ċellulari|n}}, {{W-|mt|ċellulari|n}}
* {{Z|mi}}: {{W-|mi|waea pūkoro}}
* {{Z|hu}}: {{W+|hu|mobiltelefon}}, {{W+|hu|mobil}}
* {{Z|mn}}:
*: Kirîlî: {{W-|mn|гар утас}}, {{W-|mn|хармаа дянхуа}}
*: Mongolî: {{W-|mn|ᠭᠠᠷ ᠣᠲᠠᠰᠤ}}, {{W-|mn|ᠬᠠᠷᠮᠠᠨ ᠳ᠋ᠢᠶᠠᠩᠬᠤᠸᠠ}}
* {{Z|nah}}: {{W-|nah|ehecatlanonotzqui}}
* {{Z|nv}}: {{W-|nv|biłńjoobałí}}, {{W-|nv|bił níjoobałí}}, {{W-|nv|bił níjooyisí}}, {{W-|nv|béésh bee haneʼé tʼóó naatʼáhígíí}}
* {{Z|nrf}}: {{W-|nrf|téléphône dé pouchette|n}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|mobiltelefon|n}}, {{W+|nb|mobil|n}}
*: {{Z|nn}}: {{W-|nn|mobiltelefon|n}}, {{W-|nn|mobil|n}}
* {{Z|oc}}: {{W-|oc|telefòn mobil|n}}
* {{Z|or}}: {{W-|or|ମୋବାଇଲ ଫୋନ}}
* {{Z|ug}}: {{W-|ug|يانفون}}
* {{Z|uz}}: {{W+|uz|mobil telefon}}
* {{Z|ps}}: {{W-|ps|ګرځنده ټيلفون|n|tr=garjanda ṭelefon}}
* {{Z|phl}}: {{W-|phl|waarlís}}
* {{Z|pl}}: {{W+|pl|telefon|m-in}}, {{W+|pl|telefon komórkowy|m-in}}, {{W+|pl|komórka|m}}
* {{Z|pt}}: {{W-|pt|telefone móvel|n}}, {{W+|pt|telemóvel|n}}, {{W+|pt|telefone celular|n}}, {{W+|pt|celular|n}}
* {{Z|pa}}: {{W-|pa|ਮੋਬਾਈਲ ਫ਼ੋਨ}}
* {{Z|kk}}: {{W-|kk|ұялы телефон}}, {{W-|kk|қалта телефоны}}, {{W+|kk|қалтафон}}, {{W-|kk|мобилді телефон}}
* {{Z|rm}}: {{W-|rm|telefon mobil|n}}
* {{Z|ro}}: {{W-|ro|telefon mobil|nt}}, {{W+|ro|telefon celular|nt}}, {{W+|ro|mobil|nt}}, {{W+|ro|celular|nt}}
* {{Z|ru}}: {{W+|ru|моби́льный телефо́н|n}}, {{W+|ru|со́товый телефо́н|n}}, {{W+|ru|моби́льник|n}}, {{W+|ru|со́товый|m}}, {{W+|ru|моби́ла|m}}
* {{Z|se}}: {{W-|se|mobiilatelefovdna}}, {{W-|se|giehtatelefovdna}}, {{W-|se|mátketelefovdna}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|мо̀бител|n}}, {{W-|sh|мо̏билан телѐфон|n}}, {{W-|sh|мобилни телефон|n}}
*: Latînî: {{W+|sh|mòbitel|n}}, {{W-|sh|mȍbīlan telèfōn|n}}, {{W+|sh|mobilni telefon|n}}
* {{Z|sk}}: {{W-|sk|mobilný telefón|n}}, {{W-|sk|mobil|n}}
* {{Z|sl}}: {{W-|sl|mobilni telefon|n}}, {{W-|sl|prenosni telefon|n}}
* {{Z|st}}: {{W-|st|selfono}}
* {{Z|es}}: {{W-|es|teléfono móvil}}, {{W+|es|móvil|n}}, {{W-|es|teléfono celular}}, {{W+|es|celular|n}}
* {{Z|sw}}: {{W+|sw|rununu}}, {{W-|sw|simu ya mkononi}}
* {{Z|sv}}: {{W+|sv|mobiltelefon|g}}, {{W+|sv|mobil|g}}, {{W+|sv|nalle|g}}
* {{Z|tg}}: {{W-|tg|телефони ҳамроҳ}}
* {{Z|tl}}: {{W-|tl|selepono}}, {{W+|tl|teleponong selular}}, {{W-|tl|selpon}}
* {{Z|ta}}: {{W-|ta|நகர்பேசி}}, {{W-|ta|செல்லிடத் தொலைபேசி}}
* {{Z|th}}: {{W+|th|โทรศัพท์เคลื่อนที่}}, {{W+|th|โทรศัพท์มือถือ}}, {{W+|th|มือถือ}}
* {{Z|te}}: {{W+|te|చరవాణి}}, {{W-|te|మొబైల్ ఫోన్}}
* {{Z|tt}}: {{W-|tt|кесә телефоны}}
* {{Z|tr}}: {{W+|tr|cep telefonu}}, {{W+|tr|cep}}
* {{Z|tk}}: {{W-|tk|ykjam telefon}}
* {{Z|bo}}: {{W-|bo|ལག་ཐོག་ཁ་དཔར}}
* {{Z|tpi}}: {{W-|tpi|mobail fon}}
* {{Z|uk}}: {{W-|uk|мобі́льний телефо́н|n}}, {{W-|uk|стільнико́вий телефо́н|n}}, {{W-|uk|мобі́льник|n}}
* {{Z|vi}}: {{W+|vi|điện thoại di động}}, {{W-|vi|điện thoại cầm tay}}, {{W+|vi|di động}}
* {{Z|vo}}: {{W-|vo|telefonöm polovik}}
* {{Z|wa}}: {{W+|wa|axhlåve|n}}, {{W-|wa|mobilofone|n}}, {{W-|wa|wizesse|n}}, {{W+|wa|schoûtrece|m}}, {{W-|wa|GSM|n}}
* {{Z|cy}}: {{W+|cy|ffôn symudol|n}}
* {{Z|km}}: {{W-|km|ទូរសព្ទចល័ត}}
* {{Z|sah}}: {{W-|sah|сиэп төлөппүөнэ}}
* {{Z|yi}}: {{W-|yi|מאָביל טעלעפֿאָן|n}}, {{W-|yi|מאָבילקע|m}}, {{W-|yi|צעלקע}}
* {{Z|el}}: {{W+|el|κινητό τηλέφωνο|nt}}, {{W+|el|κινητό|nt}}
* {{Z|za}}: {{W-|za|soujgih}}
* {{Z|zu}}: {{W-|zu|umakhalekhukhwini}}
{{werger-bin}}
:{{werger çavkanî|en|mobile phone|beş=Ev qismê Wergerê|oldid=76316824}}
rlu9jgt2sczex2qjkyfg5bk4pkx2h4c
firin
0
21859
6277027
6262511
2026-07-24T01:23:42Z
Ghybu
1393
/* Navdêr */
6277027
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|fiřin}}, fi[[ř]]in, فڕن
=== Navdêr ===
{{ku-tewîn-nav|firin|mê}}
[[Wêne:Jean-François Millet (II) 005.jpg|thumb|Jinek nanî diavêje firinê.]]
{{navdêr|ku|z=m}}, {{r-ya req}}
# [[cih|Cih]]ê tê [[germ]]kirin û [[xwarin]] (bi taybetî [[nan]]) tê de tê [[biraştin]].
#: {{hevmane|ku|ocax|tifik|kuçk|argûn|kuç|qozî|xulçik|berwesêl|tenûr}}
#: {{mk|ku|Nanê '''firinê'''.|Nanê ku di firinê de hatiye [[patin]].|birêz=1}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê '''firnê''', qenc sor kirin û anîn ji Beyrim re.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Bêrût|sal=1942|rûpel=166}}
# [[dikan|Dikan]]a ku [[nan]] lê tê [[patin]] û [[firotin]].
#* {{bêmînak|ku}}
==== Formên alternatîv ====
{{kol3|ku|cure=Herwiha
|ferrin
|ferin
|fern
|ferrn
|ferrne
|ferne
|firrin
|firn
|firrn
|firna
|firne
|firrna
|firrne
|furrin
|furrn
|furna
|furne
|furrna
|furrne
|furun
|furrun
}}
==== Bide ber ====
* {{g|ku|firîn}}
==== Têkildar ====
* {{g|ku|sêl}}
==== Jê ====
* {{g|ku|firneyî}}
==== Etîmolojî ====
Ji {{ji|el|ku|φούρνος|tr=fournos}} ji {{ji|la|ku|furnus}} ji {{ji|ine-pro|ku|-}} *[[gʷhorno-]] ji [[*gʷher-]] ([[germ]] kirin). Heman peyva latînî serekaniya [[four]] û [[fournaise]] yên fransî û [[furnace]] ya îngilîzî ye.
==== Werger ====
{{werger-ser|sopeya ku nan tê de tê pehtin|1}}
* {{Z|af}}: {{W-|af|oond}}
* {{Z|sq}}: {{W+|sq|furrë|m}}
* {{Z|de}}: {{W+|de|Ofen|n}}
*: {{Z|gmw-cfr}}: {{W-|gmw-cfr|Ovve|n}}
* {{Z|an}}: {{W-|an|forno|n}}
* {{Z|aii}}: {{W-|aii|ܐܲܬܘܼܢܵܐ|n|tr=āttuna}}
* {{Z|rup}}: {{W-|rup|furnu|nt}}, {{W-|rup|cireap|nt}}, {{W-|rup|cuptor|nt}}, {{W-|rup|cãminji|m}}
* {{Z|as}}: {{W-|as|চৌকা}}, {{W-|as|আখা}}, {{W-|as|ঘৰা}}, {{W-|as|অভেন}}
* {{Z|ast}}: {{W-|ast|fornu|n}}
* {{Z|az}}: {{W-|az|peç}}, {{W-|az|duxovka}}, {{W+|az|ocak}}
* {{Z|eu}}: {{W-|eu|labe}}
* {{Z|ba}}: {{W-|ba|мейес}}
* {{Z|be}}: {{W-|be|печ|m}}, {{W-|be|пе́чка|m}}
* {{Z|bn}}: {{W+|bn|চুলা}}, {{W+|bn|তন্দুর}}, {{W+|bn|উনান}}, {{W+|bn|আখা}}
* {{Z|bg}}: {{W-|bg|фу́рна|m}}, {{W+|bg|пещ|m}}
* {{Z|my}}: {{W+|my|ပေါင်းဖို}}
* {{Z|cs}}: {{W+|cs|trouba|m}}, {{W+|cs|pec|m}}
* {{Z|chr}}: {{W-|chr|ᏗᎦᏘᏗᎢ}}
* {{Z|ny}}: {{W-|ny|uvuni}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|焗爐|tr=guk6 lou4}}
*: {{Z|cmn}}: {{W+|cmn|烤爐|tr=kǎolú}}, {{W+|cmn|爐|tr=lú}}, {{W-|cmn|烘爐|tr=hōnglú}}
*: {{Z|nan}}: {{W+|nan|烘爐|tr=hang-lô͘}}
* {{Z|da}}: {{W+|da|ovn|g}}
* {{Z|id}}: {{W+|id|oven}}, {{W+|id|tanur}}
* {{Z|ar}}: {{W-|ar|فُرْن|n}}, {{W-|ar|تَنُّور|n}}
*: {{Z|arz}}: {{W-|arz|فرن|n|tr=furn}}
*: {{Z|acw}}: {{W-|acw|فُرُن|n|tr=furun}}
*: {{Z|ary}}: {{W-|ary|فرّان|n|tr=farrān}}
* {{Z|hy}}: {{W+|hy|փուռ}}, {{W+|hy|վառարան}}, {{W+|hy|ջեռոց}}
* {{Z|eo}}: {{W-|eo|forno}}, {{W-|eo|bakujo}}
* {{Z|et}}: {{W+|et|ahi}}
* {{Z|fa}}: {{W+|fa|تنور|tr=tanur}}
* {{Z|fi}}: {{W+|fi|uuni}}
* {{Z|fr}}: {{W+|fr|four|n}}
* {{Z|fur}}: {{W-|fur|fôr|n}}
* {{Z|fy}}: {{W-|fy|ûne|g}}
* {{Z|gd}}: {{W-|gd|àmhainn|m}}
* {{Z|gl}}: {{W+|gl|forno|n}}
* {{Z|got}}: {{W-|got|𐌰𐌿𐌷𐌽𐍃|n}}
* {{Z|ka}}: {{W+|ka|ღუმელი}}
* {{Z|ha}}: {{W-|ha|obin}}
* {{Z|hi}}: {{W-|hi|ओवन}}, {{W+|hi|चूल्हा|n}}, {{W-|hi|तन्दूर|n}}
* {{Z|nl}}: {{W+|nl|oven|n}}
* {{Z|he}}: {{W+|he|תַנוּר|n|tr=tanur}}
* {{Z|io}}: {{W+|io|forno}}, {{W+|io|bakoforno}}
* {{Z|ang}}: {{W-|ang|ofn|n}}
* {{Z|en}}: {{W+|en|oven}}
* {{Z|ga}}: {{W+|ga|oigheann|n}}
* {{Z|ist}}: {{W-|ist|furno|n}}
* {{Z|it}}: {{W+|it|forno|n}}
* {{Z|is}}: {{W+|is|ofn|n}}
* {{Z|ja}}: {{W+|ja|オーブン|tr=ōbun}}
* {{Z|ca}}: {{W+|ca|forn|n}}
* {{Z|ky}}: {{W-|ky|меш}}, {{W-|ky|печь}}, {{W-|ky|печка}}
* {{Z|si}}: {{W-|si|අවන්}}
* {{Z|ko}}: {{W+|ko|오븐}}
* {{Z|kw}}: {{W-|kw|forn|m}}
* {{Z|la}}: {{W+|la|furnus|n}}, {{W-|la|fornāx|m}}
* {{Z|lv}}: {{W+|lv|krāsns|m}}
* {{Z|lo}}: {{W-|lo|ເຕົາອົບ}}, {{W-|lo|ເຕົາ}}
* {{Z|lt}}: {{W-|lt|krosnis|n}}
* {{Z|mk}}: {{W-|mk|ре́рна|m}}, {{W-|mk|пе́чка|m}}, {{W-|mk|фурна|m}}
* {{Z|ms}}: {{W-|ms|ketuhar}}
* {{Z|mi}}: {{W-|mi|umu}}, {{W-|mi|oumu}}
* {{Z|hu}}: {{W+|hu|kemence}}, {{W+|hu|sütő}}
* {{Z|mn}}: {{W+|mn|зуух}}
* {{Z|nci}}: {{W-|nci|texcalli}}, {{W-|nci|tlaxcaltexcalli}}
* {{Z|nap}}: {{W-|nap|furno}}
* {{Z|nds}}:
*: {{Z|nds-de}}: {{W-|nds-de|Aven}}
* {{Z|ne}}: {{W-|ne|चुल्हो}}
* {{Z|nrf}}: {{W-|nrf|fou|n}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|ovn|n}}, {{W-|nb|stekeovn|n}}
*: {{Z|nn}}: {{W-|nn|omn|n}}
* {{Z|oc}}: {{W+|oc|forn|n}}
* {{Z|ota}}: {{W-|ota|فرون|tr=furun}}
* {{Z|ug}}: {{W-|ug|ئوچاق}}, {{W-|ug|دۇخوپكا}}, {{W-|ug|تونۇر}}
* {{Z|uz}}: {{W+|uz|pechka}}, {{W+|uz|pech}}, {{W+|uz|oʻchoq}}, {{W+|uz|duxovka}}
* {{Z|ps}}: {{W+|ps|تنور|n|tr=tanúr}}
* {{Z|pdt}}: {{W-|pdt|Owen|n}}
* {{Z|pox}}: {{W-|pox|pic|m}}
* {{Z|pl}}: {{W+|pl|piekarnik|n}}, {{W+|pl|piec|n}}
* {{Z|pt}}: {{W+|pt|forno|n}}
* {{Z|kk}}: {{W-|kk|пеш}}
* {{Z|rm}}: {{W-|rm|furn|n}}, {{W-|rm|fuorn|n}}, {{W-|rm|furnel|n}}, {{W-|rm|pigna|m}}, {{W-|rm|pegna|m}}
* {{Z|rom}}: {{W-|rom|bov|n}}
* {{Z|ro}}: {{W+|ro|cuptor|nt}}, {{W+|ro|sobă|m}}
* {{Z|ru}}: {{W+|ru|печь|m}}, {{W+|ru|пе́чка|m}}, {{W+|ru|духо́вка|m}}
* {{Z|rue}}: {{W-|rue|блóвдер|n}}
* {{Z|se}}: {{W-|se|omman}}
* {{Z|sc}}: {{W-|sc|forru|n}}, {{W-|sc|furru|n}}
* {{Z|scn}}: {{W-|scn|furneddu|n}}
* {{Z|szl}}: {{W-|szl|bratruła|m}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|пећ|m}}
*: Latînî: {{W+|sh|peć|m}}
* {{Z|sk}}: {{W-|sk|piecka|m}}, {{W-|sk|pec|m}}
* {{Z|sl}}: {{W+|sl|peč|m}}, {{W-|sl|pečica|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|pjac|n}}, {{W-|dsb|kamjeny|p}}
* {{Z|es}}: {{W+|es|horno|n}}
* {{Z|sv}}: {{W+|sv|ugn|g}}
* {{Z|tg}}: {{W+|tg|танӯр}}, {{W+|tg|печка}}
* {{Z|ta}}: {{W+|ta|அடுப்பு}}
* {{Z|rif}}: {{W-|rif|tafqunt|m}}
* {{Z|th}}: {{W+|th|เตาอบ}}
* {{Z|te}}: {{W+|te|పొయ్యి}}
* {{Z|crh}}: {{W-|crh|furun}}, {{W-|crh|pırım}}
* {{Z|tt}}: {{W-|tt|фурун}}
* {{Z|tr}}: {{W+|tr|fırın}}, {{W+|tr|ocak}}
* {{Z|tk}}: {{W-|tk|peç}}, {{W-|tk|ojak}}
* {{Z|udm}}: {{W-|udm|гур}}
* {{Z|uga}}: {{W-|uga|𐎚𐎐𐎐𐎗}}
* {{Z|ur}}: {{W-|ur|تندور|n|tr=tandūr}}
* {{Z|uk}}: {{W+|uk|піч|m}}, {{W-|uk|пі́чка|m}}
* {{Z|vep}}: {{W-|vep|päč}}
* {{Z|vi}}: {{W+|vi|lò}}, {{W-|vi|lò nướng}}
* {{Z|vo}}: {{W+|vo|furnod}}
* {{Z|wa}}: {{W+|wa|for|n}}
* {{Z|cy}}: {{W+|cy|ffwrn|m}}, {{W+|cy|popty|n}}
* {{Z|km}}: {{W+|km|ឡ}}
* {{Z|yi}}: {{W-|yi|אויוון}}
* {{Z|pnt}}: {{W-|pnt|φουρνίν|nt}}
* {{Z|el}}: {{W+|el|φούρνος|n}}
*: Kevn: {{W-|grc|ἰπνός|n}}, {{W-|grc|κάμινος|n|m}}, {{W-|grc|κλίβανος|n}}
* {{Z|za}}: {{W-|za|gaujluz}}
{{werger-bin}}
:{{werger çavkanî|en|oven|beş=Ev qismê Wergerê|oldid=76292557}}
{{werger-ser|'''dikana nanî dipêje û difiroşe'''|2}}
* {{Z|sq}}: {{W-|sq|dyqan buke}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|báń áldeʼyú}}
* {{Z|de}}: {{W+|de|Bäckerei|m}}, {{W+|de|Backstube|m}}
* {{Z|ast}}: {{W-|ast|panadería|m}}
* {{Z|az}}: {{W-|az|çörəkxana}}
* {{Z|eu}}: {{W+|eu|okindegi}}, {{W+|eu|ogitegi}}
* {{Z|be}}: {{W-|be|пяка́рня|m|sc=Cyrl}}
* {{Z|bn}}: {{W-|bn|বেকারি}}
* {{Z|bg}}: {{W-|bg|фу́рна|m|sc=Cyrl}}, {{W-|bg|пека́рна|m|sc=Cyrl}}, {{W-|bg|хлеба́рница|m|sc=Cyrl}}
* {{Z|my}}: {{W-|my|မုန့်ဖုတ်ရုံ|sc=Mymr}}
* {{Z|cs}}: {{W+|cs|pekárna|m}}, {{W+|cs|pekařství|nt}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W-|cmn|麵包店|tr=miànbāodiàn}}, {{W-|cmn|麵包房|tr=miànbāofáng}}
* {{Z|da}}: {{W+|da|bageri|nt}}
* {{Z|id}}: {{W-|id|toko roti}}, {{W-|id|bakeri}}
* {{Z|ar}}: {{W-|ar|مَخْبِز|n}}
*: {{Z|ary}}: {{W-|ary|مخبزة|tr=maḵbaza}}
* {{Z|hy}}: {{W+|hy|փուռ|sc=Armn}}
* {{Z|eo}}: {{W-|eo|bakejo}}
* {{Z|et}}: {{W-|et|pagaritööstus}}
* {{Z|fa}}: {{W+|fa|نانوائی|tr=nânvâ'i, nânevâ'i}}, {{W-|fa|دکان نانوائی|tr=dokân nânevâ'i}}
* {{Z|fi}}: {{W+|fi|leipomo}}
* {{Z|fr}}: {{W+|fr|boulangerie|m}}
* {{Z|gl}}: {{W+|gl|panadaría|m}}, {{W+|gl|panadería|m}}, {{W+|gl|forno|n}}, {{W+|gl|tafona|m}}
* {{Z|ka}}: {{W-|ka|ფურნე}}, {{W-|ka|საფუნთუშე}}
* {{Z|hi}}: {{W-|hi|बेकरी|m}}
* {{Z|nl}}: {{W+|nl|bakkerij|m}}
* {{Z|he}}: {{W+|he|מַאֲפִיָּה|m|tr=maafia}}
* {{Z|io}}: {{W+|io|bakerio}}
* {{Z|ang}}: {{W-|ang|bæchūs|nt}}, {{W-|ang|bæcern|nt}}
* {{Z|en}}: {{W+|en|bakery}}
* {{Z|it}}: {{W+|it|panetteria|m}}, {{W+|it|forno|n}}, {{W+|it|panificio|n}}
* {{Z|is}}: {{W-|is|bakarí|nt}}
* {{Z|ja}}: {{W+|ja|パン屋|tr=ぱんや, pan'ya}}, {{W+|ja|ベーカリー|tr=bēkarī|sc=Jpan}}
* {{Z|ca}}: {{W+|ca|fleca|m}}, {{W+|ca|forn|n}}
* {{Z|qu}}: {{W-|qu|t'anta ranqhana wasi}}
* {{Z|ky}}: {{W+|ky|наабайкана|sc=Cyrl}}
* {{Z|ko}}: {{W+|ko|빵집}}, {{W+|ko|제과점}}, {{W-|ko|베이커리}}
* {{Z|la}}: {{W-|la|pistrīna|m}}
* {{Z|lv}}: {{W-|lv|maiznīca|m}}
* {{Z|lo}}: {{W-|lo|ຮ້ານຂາຍເຂົ້າຈີ່}}
* {{Z|lt}}: {{W-|lt|kepykla|m}}
* {{Z|mk}}: {{W-|mk|пека́рница|m}}, {{W-|mk|фу́рна|m}}, {{W-|mk|леба́рница|m}}
* {{Z|ms}}: {{W-|ms|kedai roti}}
* {{Z|mt}}: {{W-|mt|forn}}
* {{Z|mi}}: {{W-|mi|hereumu parāoa}}, {{W-|mi|hereumu}}
* {{Z|hu}}: {{W+|hu|kenyérbolt}}, {{W+|hu|péküzlet}}, {{W+|hu|pékség}}
* {{Z|mn}}: {{W-|mn|талх барих газар|sc=Cyrl}}
* {{Z|nv}}: {{W-|nv|bááh álʼį́įgi}}
* {{Z|nrf}}: {{W-|nrf|boulangu'thie|m}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|bakeri|nt}}
* {{Z|ota}}: {{W-|ota|فرن|tr=fırın|sc=ota-Arab}}
* {{Z|uz}}: {{W+|uz|novvoyxona}}
* {{Z|pdt}}: {{W-|pdt|Bakjarie|m}}
* {{Z|pl}}: {{W+|pl|piekarnia|m}}
* {{Z|pt}}: {{W+|pt|padaria|m}}
* {{Z|kk}}: {{W-|kk|наубайхана|sc=Cyrl}}, {{W-|kk|нанхана|sc=Cyrl}}
* {{Z|ro}}: {{W+|ro|brutărie|m}}
* {{Z|ru}}: {{W+|ru|пека́рня|m}}, {{W+|ru|хлебопека́рня|m}}, {{W+|ru|бу́лочная|m}}
* {{Z|scn}}: {{W+|scn|furnu|n}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|пекара|m|sc=Cyrl}}, {{W-|sh|пекарница|m|sc=Cyrl}}
*: Latînî: {{W+|sh|pekara|m}}, {{W+|sh|pekarnica|m}}
* {{Z|sk}}: {{W+|sk|pekáreň|m}}
* {{Z|sl}}: {{W+|sl|pekarna|m}}
* {{Z|es}}: {{W+|es|panadería|m}}, {{W-|es|tahona|m}}, {{W+|es|horno|n}}
* {{Z|sv}}: {{W+|sv|bageri|nt}}
* {{Z|tg}}: {{W-|tg|нонвойхона}}, {{W-|tg|дӯкони хаб}}
* {{Z|tl}}: {{W-|tl|panaderya}}, {{W-|tl|tinapayan}}
* {{Z|th}}: {{W+|th|ร้านขนมปัง|tr=ráan kà-nǒm bpang}}, {{W+|th|เบเกอรี|tr=bee-gəə-rii}}
* {{Z|tr}}: {{W+|tr|fırın}}, {{W+|tr|pastane}}
* {{Z|tk}}: {{W-|tk|çörekhana}}
* {{Z|ur}}: {{W-|ur|بیکری|m|tr=bekrī|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|пека́рня|m|sc=Cyrl}}
* {{Z|vi}}: {{W-|vi|lò bánh mì}}, {{W-|vi|tiệm bánh}}
* {{Z|vo}}: {{W+|vo|baköp}}
* {{Z|wa}}: {{W+|wa|boledjreye|m}}
* {{Z|cy}}: {{W-|cy|siop fara|m}}, {{W-|cy|becws|n}}
* {{Z|km}}: {{W-|km|ឡដុតនំប៉័ង|tr=lɑɑ dot num paŋ|sc=Khmr}}
* {{Z|yi}}: {{W-|yi|בעקערײַ|m|sc=Hebr}}
* {{Z|el}}: {{W+|el|φούρνος|n}}, {{W+|el|αρτοποιείο|nt|sc=Grek}}
*: Kevn: {{W-|grc|ἀρτοπώλιον|nt}}
{{werger-bin}}
:{{werger çavkanî|en|bakery|beş=Ev qismê Wergerê|oldid=76469064}}
rmze70i241npsff463vaqgu8uj6tbkt
6277033
6277027
2026-07-24T01:31:58Z
Ghybu
1393
/* Navdêr */
6277033
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|fiřin}}, fi[[ř]]in, فڕن
=== Navdêr ===
{{ku-tewîn-nav|firin|mê}}
[[Wêne:Jean-François Millet (II) 005.jpg|thumb|Jinek nanî diavêje firinê.]]
{{navdêr|ku|z=m}}, {{r-ya req}}
# [[cih|Cih]]ê tê [[germ]]kirin û [[xwarin]] (bi taybetî [[nan]]) tê de tê [[biraştin]].
#: {{hevmane|ku|ocax|tifik|kuçk|argûn|kuç|qozî|xulçik|berwesêl|tenûr}}
#: {{mk|ku|Nanê '''firinê'''.|Nanê ku di firinê de hatiye [[patin]].|birêz=1}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê '''firnê''', qenc sor kirin û anîn ji Beyrim re.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Parîs|sal=1940|rûpel=166}}
# [[dikan|Dikan]]a ku [[nan]] lê tê [[patin]] û [[firotin]].
#* {{bêmînak|ku}}
==== Formên alternatîv ====
{{kol3|ku|cure=Herwiha
|ferrin
|ferin
|fern
|ferrn
|ferrne
|ferne
|firrin
|firn
|firrn
|firna
|firne
|firrna
|firrne
|furrin
|furrn
|furna
|furne
|furrna
|furrne
|furun
|furrun
}}
==== Bide ber ====
* {{g|ku|firîn}}
==== Têkildar ====
* {{g|ku|sêl}}
==== Jê ====
* {{g|ku|firneyî}}
==== Etîmolojî ====
Ji {{ji|el|ku|φούρνος|tr=fournos}} ji {{ji|la|ku|furnus}} ji {{ji|ine-pro|ku|-}} *[[gʷhorno-]] ji [[*gʷher-]] ([[germ]] kirin). Heman peyva latînî serekaniya [[four]] û [[fournaise]] yên fransî û [[furnace]] ya îngilîzî ye.
==== Werger ====
{{werger-ser|sopeya ku nan tê de tê pehtin|1}}
* {{Z|af}}: {{W-|af|oond}}
* {{Z|sq}}: {{W+|sq|furrë|m}}
* {{Z|de}}: {{W+|de|Ofen|n}}
*: {{Z|gmw-cfr}}: {{W-|gmw-cfr|Ovve|n}}
* {{Z|an}}: {{W-|an|forno|n}}
* {{Z|aii}}: {{W-|aii|ܐܲܬܘܼܢܵܐ|n|tr=āttuna}}
* {{Z|rup}}: {{W-|rup|furnu|nt}}, {{W-|rup|cireap|nt}}, {{W-|rup|cuptor|nt}}, {{W-|rup|cãminji|m}}
* {{Z|as}}: {{W-|as|চৌকা}}, {{W-|as|আখা}}, {{W-|as|ঘৰা}}, {{W-|as|অভেন}}
* {{Z|ast}}: {{W-|ast|fornu|n}}
* {{Z|az}}: {{W-|az|peç}}, {{W-|az|duxovka}}, {{W+|az|ocak}}
* {{Z|eu}}: {{W-|eu|labe}}
* {{Z|ba}}: {{W-|ba|мейес}}
* {{Z|be}}: {{W-|be|печ|m}}, {{W-|be|пе́чка|m}}
* {{Z|bn}}: {{W+|bn|চুলা}}, {{W+|bn|তন্দুর}}, {{W+|bn|উনান}}, {{W+|bn|আখা}}
* {{Z|bg}}: {{W-|bg|фу́рна|m}}, {{W+|bg|пещ|m}}
* {{Z|my}}: {{W+|my|ပေါင်းဖို}}
* {{Z|cs}}: {{W+|cs|trouba|m}}, {{W+|cs|pec|m}}
* {{Z|chr}}: {{W-|chr|ᏗᎦᏘᏗᎢ}}
* {{Z|ny}}: {{W-|ny|uvuni}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|焗爐|tr=guk6 lou4}}
*: {{Z|cmn}}: {{W+|cmn|烤爐|tr=kǎolú}}, {{W+|cmn|爐|tr=lú}}, {{W-|cmn|烘爐|tr=hōnglú}}
*: {{Z|nan}}: {{W+|nan|烘爐|tr=hang-lô͘}}
* {{Z|da}}: {{W+|da|ovn|g}}
* {{Z|id}}: {{W+|id|oven}}, {{W+|id|tanur}}
* {{Z|ar}}: {{W-|ar|فُرْن|n}}, {{W-|ar|تَنُّور|n}}
*: {{Z|arz}}: {{W-|arz|فرن|n|tr=furn}}
*: {{Z|acw}}: {{W-|acw|فُرُن|n|tr=furun}}
*: {{Z|ary}}: {{W-|ary|فرّان|n|tr=farrān}}
* {{Z|hy}}: {{W+|hy|փուռ}}, {{W+|hy|վառարան}}, {{W+|hy|ջեռոց}}
* {{Z|eo}}: {{W-|eo|forno}}, {{W-|eo|bakujo}}
* {{Z|et}}: {{W+|et|ahi}}
* {{Z|fa}}: {{W+|fa|تنور|tr=tanur}}
* {{Z|fi}}: {{W+|fi|uuni}}
* {{Z|fr}}: {{W+|fr|four|n}}
* {{Z|fur}}: {{W-|fur|fôr|n}}
* {{Z|fy}}: {{W-|fy|ûne|g}}
* {{Z|gd}}: {{W-|gd|àmhainn|m}}
* {{Z|gl}}: {{W+|gl|forno|n}}
* {{Z|got}}: {{W-|got|𐌰𐌿𐌷𐌽𐍃|n}}
* {{Z|ka}}: {{W+|ka|ღუმელი}}
* {{Z|ha}}: {{W-|ha|obin}}
* {{Z|hi}}: {{W-|hi|ओवन}}, {{W+|hi|चूल्हा|n}}, {{W-|hi|तन्दूर|n}}
* {{Z|nl}}: {{W+|nl|oven|n}}
* {{Z|he}}: {{W+|he|תַנוּר|n|tr=tanur}}
* {{Z|io}}: {{W+|io|forno}}, {{W+|io|bakoforno}}
* {{Z|ang}}: {{W-|ang|ofn|n}}
* {{Z|en}}: {{W+|en|oven}}
* {{Z|ga}}: {{W+|ga|oigheann|n}}
* {{Z|ist}}: {{W-|ist|furno|n}}
* {{Z|it}}: {{W+|it|forno|n}}
* {{Z|is}}: {{W+|is|ofn|n}}
* {{Z|ja}}: {{W+|ja|オーブン|tr=ōbun}}
* {{Z|ca}}: {{W+|ca|forn|n}}
* {{Z|ky}}: {{W-|ky|меш}}, {{W-|ky|печь}}, {{W-|ky|печка}}
* {{Z|si}}: {{W-|si|අවන්}}
* {{Z|ko}}: {{W+|ko|오븐}}
* {{Z|kw}}: {{W-|kw|forn|m}}
* {{Z|la}}: {{W+|la|furnus|n}}, {{W-|la|fornāx|m}}
* {{Z|lv}}: {{W+|lv|krāsns|m}}
* {{Z|lo}}: {{W-|lo|ເຕົາອົບ}}, {{W-|lo|ເຕົາ}}
* {{Z|lt}}: {{W-|lt|krosnis|n}}
* {{Z|mk}}: {{W-|mk|ре́рна|m}}, {{W-|mk|пе́чка|m}}, {{W-|mk|фурна|m}}
* {{Z|ms}}: {{W-|ms|ketuhar}}
* {{Z|mi}}: {{W-|mi|umu}}, {{W-|mi|oumu}}
* {{Z|hu}}: {{W+|hu|kemence}}, {{W+|hu|sütő}}
* {{Z|mn}}: {{W+|mn|зуух}}
* {{Z|nci}}: {{W-|nci|texcalli}}, {{W-|nci|tlaxcaltexcalli}}
* {{Z|nap}}: {{W-|nap|furno}}
* {{Z|nds}}:
*: {{Z|nds-de}}: {{W-|nds-de|Aven}}
* {{Z|ne}}: {{W-|ne|चुल्हो}}
* {{Z|nrf}}: {{W-|nrf|fou|n}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|ovn|n}}, {{W-|nb|stekeovn|n}}
*: {{Z|nn}}: {{W-|nn|omn|n}}
* {{Z|oc}}: {{W+|oc|forn|n}}
* {{Z|ota}}: {{W-|ota|فرون|tr=furun}}
* {{Z|ug}}: {{W-|ug|ئوچاق}}, {{W-|ug|دۇخوپكا}}, {{W-|ug|تونۇر}}
* {{Z|uz}}: {{W+|uz|pechka}}, {{W+|uz|pech}}, {{W+|uz|oʻchoq}}, {{W+|uz|duxovka}}
* {{Z|ps}}: {{W+|ps|تنور|n|tr=tanúr}}
* {{Z|pdt}}: {{W-|pdt|Owen|n}}
* {{Z|pox}}: {{W-|pox|pic|m}}
* {{Z|pl}}: {{W+|pl|piekarnik|n}}, {{W+|pl|piec|n}}
* {{Z|pt}}: {{W+|pt|forno|n}}
* {{Z|kk}}: {{W-|kk|пеш}}
* {{Z|rm}}: {{W-|rm|furn|n}}, {{W-|rm|fuorn|n}}, {{W-|rm|furnel|n}}, {{W-|rm|pigna|m}}, {{W-|rm|pegna|m}}
* {{Z|rom}}: {{W-|rom|bov|n}}
* {{Z|ro}}: {{W+|ro|cuptor|nt}}, {{W+|ro|sobă|m}}
* {{Z|ru}}: {{W+|ru|печь|m}}, {{W+|ru|пе́чка|m}}, {{W+|ru|духо́вка|m}}
* {{Z|rue}}: {{W-|rue|блóвдер|n}}
* {{Z|se}}: {{W-|se|omman}}
* {{Z|sc}}: {{W-|sc|forru|n}}, {{W-|sc|furru|n}}
* {{Z|scn}}: {{W-|scn|furneddu|n}}
* {{Z|szl}}: {{W-|szl|bratruła|m}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|пећ|m}}
*: Latînî: {{W+|sh|peć|m}}
* {{Z|sk}}: {{W-|sk|piecka|m}}, {{W-|sk|pec|m}}
* {{Z|sl}}: {{W+|sl|peč|m}}, {{W-|sl|pečica|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|pjac|n}}, {{W-|dsb|kamjeny|p}}
* {{Z|es}}: {{W+|es|horno|n}}
* {{Z|sv}}: {{W+|sv|ugn|g}}
* {{Z|tg}}: {{W+|tg|танӯр}}, {{W+|tg|печка}}
* {{Z|ta}}: {{W+|ta|அடுப்பு}}
* {{Z|rif}}: {{W-|rif|tafqunt|m}}
* {{Z|th}}: {{W+|th|เตาอบ}}
* {{Z|te}}: {{W+|te|పొయ్యి}}
* {{Z|crh}}: {{W-|crh|furun}}, {{W-|crh|pırım}}
* {{Z|tt}}: {{W-|tt|фурун}}
* {{Z|tr}}: {{W+|tr|fırın}}, {{W+|tr|ocak}}
* {{Z|tk}}: {{W-|tk|peç}}, {{W-|tk|ojak}}
* {{Z|udm}}: {{W-|udm|гур}}
* {{Z|uga}}: {{W-|uga|𐎚𐎐𐎐𐎗}}
* {{Z|ur}}: {{W-|ur|تندور|n|tr=tandūr}}
* {{Z|uk}}: {{W+|uk|піч|m}}, {{W-|uk|пі́чка|m}}
* {{Z|vep}}: {{W-|vep|päč}}
* {{Z|vi}}: {{W+|vi|lò}}, {{W-|vi|lò nướng}}
* {{Z|vo}}: {{W+|vo|furnod}}
* {{Z|wa}}: {{W+|wa|for|n}}
* {{Z|cy}}: {{W+|cy|ffwrn|m}}, {{W+|cy|popty|n}}
* {{Z|km}}: {{W+|km|ឡ}}
* {{Z|yi}}: {{W-|yi|אויוון}}
* {{Z|pnt}}: {{W-|pnt|φουρνίν|nt}}
* {{Z|el}}: {{W+|el|φούρνος|n}}
*: Kevn: {{W-|grc|ἰπνός|n}}, {{W-|grc|κάμινος|n|m}}, {{W-|grc|κλίβανος|n}}
* {{Z|za}}: {{W-|za|gaujluz}}
{{werger-bin}}
:{{werger çavkanî|en|oven|beş=Ev qismê Wergerê|oldid=76292557}}
{{werger-ser|'''dikana nanî dipêje û difiroşe'''|2}}
* {{Z|sq}}: {{W-|sq|dyqan buke}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|báń áldeʼyú}}
* {{Z|de}}: {{W+|de|Bäckerei|m}}, {{W+|de|Backstube|m}}
* {{Z|ast}}: {{W-|ast|panadería|m}}
* {{Z|az}}: {{W-|az|çörəkxana}}
* {{Z|eu}}: {{W+|eu|okindegi}}, {{W+|eu|ogitegi}}
* {{Z|be}}: {{W-|be|пяка́рня|m|sc=Cyrl}}
* {{Z|bn}}: {{W-|bn|বেকারি}}
* {{Z|bg}}: {{W-|bg|фу́рна|m|sc=Cyrl}}, {{W-|bg|пека́рна|m|sc=Cyrl}}, {{W-|bg|хлеба́рница|m|sc=Cyrl}}
* {{Z|my}}: {{W-|my|မုန့်ဖုတ်ရုံ|sc=Mymr}}
* {{Z|cs}}: {{W+|cs|pekárna|m}}, {{W+|cs|pekařství|nt}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W-|cmn|麵包店|tr=miànbāodiàn}}, {{W-|cmn|麵包房|tr=miànbāofáng}}
* {{Z|da}}: {{W+|da|bageri|nt}}
* {{Z|id}}: {{W-|id|toko roti}}, {{W-|id|bakeri}}
* {{Z|ar}}: {{W-|ar|مَخْبِز|n}}
*: {{Z|ary}}: {{W-|ary|مخبزة|tr=maḵbaza}}
* {{Z|hy}}: {{W+|hy|փուռ|sc=Armn}}
* {{Z|eo}}: {{W-|eo|bakejo}}
* {{Z|et}}: {{W-|et|pagaritööstus}}
* {{Z|fa}}: {{W+|fa|نانوائی|tr=nânvâ'i, nânevâ'i}}, {{W-|fa|دکان نانوائی|tr=dokân nânevâ'i}}
* {{Z|fi}}: {{W+|fi|leipomo}}
* {{Z|fr}}: {{W+|fr|boulangerie|m}}
* {{Z|gl}}: {{W+|gl|panadaría|m}}, {{W+|gl|panadería|m}}, {{W+|gl|forno|n}}, {{W+|gl|tafona|m}}
* {{Z|ka}}: {{W-|ka|ფურნე}}, {{W-|ka|საფუნთუშე}}
* {{Z|hi}}: {{W-|hi|बेकरी|m}}
* {{Z|nl}}: {{W+|nl|bakkerij|m}}
* {{Z|he}}: {{W+|he|מַאֲפִיָּה|m|tr=maafia}}
* {{Z|io}}: {{W+|io|bakerio}}
* {{Z|ang}}: {{W-|ang|bæchūs|nt}}, {{W-|ang|bæcern|nt}}
* {{Z|en}}: {{W+|en|bakery}}
* {{Z|it}}: {{W+|it|panetteria|m}}, {{W+|it|forno|n}}, {{W+|it|panificio|n}}
* {{Z|is}}: {{W-|is|bakarí|nt}}
* {{Z|ja}}: {{W+|ja|パン屋|tr=ぱんや, pan'ya}}, {{W+|ja|ベーカリー|tr=bēkarī|sc=Jpan}}
* {{Z|ca}}: {{W+|ca|fleca|m}}, {{W+|ca|forn|n}}
* {{Z|qu}}: {{W-|qu|t'anta ranqhana wasi}}
* {{Z|ky}}: {{W+|ky|наабайкана|sc=Cyrl}}
* {{Z|ko}}: {{W+|ko|빵집}}, {{W+|ko|제과점}}, {{W-|ko|베이커리}}
* {{Z|la}}: {{W-|la|pistrīna|m}}
* {{Z|lv}}: {{W-|lv|maiznīca|m}}
* {{Z|lo}}: {{W-|lo|ຮ້ານຂາຍເຂົ້າຈີ່}}
* {{Z|lt}}: {{W-|lt|kepykla|m}}
* {{Z|mk}}: {{W-|mk|пека́рница|m}}, {{W-|mk|фу́рна|m}}, {{W-|mk|леба́рница|m}}
* {{Z|ms}}: {{W-|ms|kedai roti}}
* {{Z|mt}}: {{W-|mt|forn}}
* {{Z|mi}}: {{W-|mi|hereumu parāoa}}, {{W-|mi|hereumu}}
* {{Z|hu}}: {{W+|hu|kenyérbolt}}, {{W+|hu|péküzlet}}, {{W+|hu|pékség}}
* {{Z|mn}}: {{W-|mn|талх барих газар|sc=Cyrl}}
* {{Z|nv}}: {{W-|nv|bááh álʼį́įgi}}
* {{Z|nrf}}: {{W-|nrf|boulangu'thie|m}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|bakeri|nt}}
* {{Z|ota}}: {{W-|ota|فرن|tr=fırın|sc=ota-Arab}}
* {{Z|uz}}: {{W+|uz|novvoyxona}}
* {{Z|pdt}}: {{W-|pdt|Bakjarie|m}}
* {{Z|pl}}: {{W+|pl|piekarnia|m}}
* {{Z|pt}}: {{W+|pt|padaria|m}}
* {{Z|kk}}: {{W-|kk|наубайхана|sc=Cyrl}}, {{W-|kk|нанхана|sc=Cyrl}}
* {{Z|ro}}: {{W+|ro|brutărie|m}}
* {{Z|ru}}: {{W+|ru|пека́рня|m}}, {{W+|ru|хлебопека́рня|m}}, {{W+|ru|бу́лочная|m}}
* {{Z|scn}}: {{W+|scn|furnu|n}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|пекара|m|sc=Cyrl}}, {{W-|sh|пекарница|m|sc=Cyrl}}
*: Latînî: {{W+|sh|pekara|m}}, {{W+|sh|pekarnica|m}}
* {{Z|sk}}: {{W+|sk|pekáreň|m}}
* {{Z|sl}}: {{W+|sl|pekarna|m}}
* {{Z|es}}: {{W+|es|panadería|m}}, {{W-|es|tahona|m}}, {{W+|es|horno|n}}
* {{Z|sv}}: {{W+|sv|bageri|nt}}
* {{Z|tg}}: {{W-|tg|нонвойхона}}, {{W-|tg|дӯкони хаб}}
* {{Z|tl}}: {{W-|tl|panaderya}}, {{W-|tl|tinapayan}}
* {{Z|th}}: {{W+|th|ร้านขนมปัง|tr=ráan kà-nǒm bpang}}, {{W+|th|เบเกอรี|tr=bee-gəə-rii}}
* {{Z|tr}}: {{W+|tr|fırın}}, {{W+|tr|pastane}}
* {{Z|tk}}: {{W-|tk|çörekhana}}
* {{Z|ur}}: {{W-|ur|بیکری|m|tr=bekrī|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|пека́рня|m|sc=Cyrl}}
* {{Z|vi}}: {{W-|vi|lò bánh mì}}, {{W-|vi|tiệm bánh}}
* {{Z|vo}}: {{W+|vo|baköp}}
* {{Z|wa}}: {{W+|wa|boledjreye|m}}
* {{Z|cy}}: {{W-|cy|siop fara|m}}, {{W-|cy|becws|n}}
* {{Z|km}}: {{W-|km|ឡដុតនំប៉័ង|tr=lɑɑ dot num paŋ|sc=Khmr}}
* {{Z|yi}}: {{W-|yi|בעקערײַ|m|sc=Hebr}}
* {{Z|el}}: {{W+|el|φούρνος|n}}, {{W+|el|αρτοποιείο|nt|sc=Grek}}
*: Kevn: {{W-|grc|ἀρτοπώλιον|nt}}
{{werger-bin}}
:{{werger çavkanî|en|bakery|beş=Ev qismê Wergerê|oldid=76469064}}
gtbl6yzk0as6bd9eeqf6d3emdv7xqni
6277042
6277033
2026-07-24T01:56:00Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6277042
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|fiřin}}, fi[[ř]]in, فڕن
=== Navdêr ===
{{ku-tewîn-nav|firin|mê}}
[[Wêne:Jean-François Millet (II) 005.jpg|thumb|Jinek nanî diavêje firinê.]]
{{navdêr|ku|z=m}}, {{r-ya req}}
# [[cih|Cih]]ê tê [[germ]]kirin û [[xwarin]] (bi taybetî [[nan]]) tê de tê [[biraştin]].
#: {{hevmane|ku|ocax|tifik|kuçk|argûn|kuç|qozî|xulçik|berwesêl|tenûr}}
#: {{mk|ku|Nanê '''firinê'''.|Nanê ku di firinê de hatiye [[patin]].|birêz=1}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê '''firnê''', qenc sor kirin û anîn ji Beyrim re.|nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Parîs|sal=1940|rûpel=166}}
# [[dikan|Dikan]]a ku [[nan]] lê tê [[patin]] û [[firotin]].
#* {{bêmînak|ku}}
==== Formên alternatîv ====
{{kol3|ku|cure=Herwiha
|ferrin
|ferin
|fern
|ferrn
|ferrne
|ferne
|firrin
|firn
|firrn
|firna
|firne
|firrna
|firrne
|furrin
|furrn
|furna
|furne
|furrna
|furrne
|furun
|furrun
}}
==== Bide ber ====
* {{g|ku|firîn}}
==== Têkildar ====
* {{g|ku|sêl}}
==== Jê ====
* {{g|ku|firneyî}}
==== Etîmolojî ====
Ji {{ji|el|ku|φούρνος|tr=fournos}} ji {{ji|la|ku|furnus}} ji {{ji|ine-pro|ku|-}} *[[gʷhorno-]] ji [[*gʷher-]] ([[germ]] kirin). Heman peyva latînî serekaniya [[four]] û [[fournaise]] yên fransî û [[furnace]] ya îngilîzî ye.
==== Werger ====
{{werger-ser|sopeya ku nan tê de tê pehtin|1}}
* {{Z|af}}: {{W-|af|oond}}
* {{Z|sq}}: {{W+|sq|furrë|m}}
* {{Z|de}}: {{W+|de|Ofen|n}}
*: {{Z|gmw-cfr}}: {{W-|gmw-cfr|Ovve|n}}
* {{Z|an}}: {{W-|an|forno|n}}
* {{Z|aii}}: {{W-|aii|ܐܲܬܘܼܢܵܐ|n|tr=āttuna}}
* {{Z|rup}}: {{W-|rup|furnu|nt}}, {{W-|rup|cireap|nt}}, {{W-|rup|cuptor|nt}}, {{W-|rup|cãminji|m}}
* {{Z|as}}: {{W-|as|চৌকা}}, {{W-|as|আখা}}, {{W-|as|ঘৰা}}, {{W-|as|অভেন}}
* {{Z|ast}}: {{W-|ast|fornu|n}}
* {{Z|az}}: {{W-|az|peç}}, {{W-|az|duxovka}}, {{W+|az|ocak}}
* {{Z|eu}}: {{W-|eu|labe}}
* {{Z|ba}}: {{W-|ba|мейес}}
* {{Z|be}}: {{W-|be|печ|m}}, {{W-|be|пе́чка|m}}
* {{Z|bn}}: {{W+|bn|চুলা}}, {{W+|bn|তন্দুর}}, {{W+|bn|উনান}}, {{W+|bn|আখা}}
* {{Z|bg}}: {{W-|bg|фу́рна|m}}, {{W+|bg|пещ|m}}
* {{Z|my}}: {{W+|my|ပေါင်းဖို}}
* {{Z|cs}}: {{W+|cs|trouba|m}}, {{W+|cs|pec|m}}
* {{Z|chr}}: {{W-|chr|ᏗᎦᏘᏗᎢ}}
* {{Z|ny}}: {{W-|ny|uvuni}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|焗爐|tr=guk6 lou4}}
*: {{Z|cmn}}: {{W+|cmn|烤爐|tr=kǎolú}}, {{W+|cmn|爐|tr=lú}}, {{W-|cmn|烘爐|tr=hōnglú}}
*: {{Z|nan}}: {{W+|nan|烘爐|tr=hang-lô͘}}
* {{Z|da}}: {{W+|da|ovn|g}}
* {{Z|id}}: {{W+|id|oven}}, {{W+|id|tanur}}
* {{Z|ar}}: {{W-|ar|فُرْن|n}}, {{W-|ar|تَنُّور|n}}
*: {{Z|arz}}: {{W-|arz|فرن|n|tr=furn}}
*: {{Z|acw}}: {{W-|acw|فُرُن|n|tr=furun}}
*: {{Z|ary}}: {{W-|ary|فرّان|n|tr=farrān}}
* {{Z|hy}}: {{W+|hy|փուռ}}, {{W+|hy|վառարան}}, {{W+|hy|ջեռոց}}
* {{Z|eo}}: {{W-|eo|forno}}, {{W-|eo|bakujo}}
* {{Z|et}}: {{W+|et|ahi}}
* {{Z|fa}}: {{W+|fa|تنور|tr=tanur}}
* {{Z|fi}}: {{W+|fi|uuni}}
* {{Z|fr}}: {{W+|fr|four|n}}
* {{Z|fur}}: {{W-|fur|fôr|n}}
* {{Z|fy}}: {{W-|fy|ûne|g}}
* {{Z|gd}}: {{W-|gd|àmhainn|m}}
* {{Z|gl}}: {{W+|gl|forno|n}}
* {{Z|got}}: {{W-|got|𐌰𐌿𐌷𐌽𐍃|n}}
* {{Z|ka}}: {{W+|ka|ღუმელი}}
* {{Z|ha}}: {{W-|ha|obin}}
* {{Z|hi}}: {{W-|hi|ओवन}}, {{W+|hi|चूल्हा|n}}, {{W-|hi|तन्दूर|n}}
* {{Z|nl}}: {{W+|nl|oven|n}}
* {{Z|he}}: {{W+|he|תַנוּר|n|tr=tanur}}
* {{Z|io}}: {{W+|io|forno}}, {{W+|io|bakoforno}}
* {{Z|ang}}: {{W-|ang|ofn|n}}
* {{Z|en}}: {{W+|en|oven}}
* {{Z|ga}}: {{W+|ga|oigheann|n}}
* {{Z|ist}}: {{W-|ist|furno|n}}
* {{Z|it}}: {{W+|it|forno|n}}
* {{Z|is}}: {{W+|is|ofn|n}}
* {{Z|ja}}: {{W+|ja|オーブン|tr=ōbun}}
* {{Z|ca}}: {{W+|ca|forn|n}}
* {{Z|ky}}: {{W-|ky|меш}}, {{W-|ky|печь}}, {{W-|ky|печка}}
* {{Z|si}}: {{W-|si|අවන්}}
* {{Z|ko}}: {{W+|ko|오븐}}
* {{Z|kw}}: {{W-|kw|forn|m}}
* {{Z|la}}: {{W+|la|furnus|n}}, {{W-|la|fornāx|m}}
* {{Z|lv}}: {{W+|lv|krāsns|m}}
* {{Z|lo}}: {{W-|lo|ເຕົາອົບ}}, {{W-|lo|ເຕົາ}}
* {{Z|lt}}: {{W-|lt|krosnis|n}}
* {{Z|mk}}: {{W-|mk|ре́рна|m}}, {{W-|mk|пе́чка|m}}, {{W-|mk|фурна|m}}
* {{Z|ms}}: {{W-|ms|ketuhar}}
* {{Z|mi}}: {{W-|mi|umu}}, {{W-|mi|oumu}}
* {{Z|hu}}: {{W+|hu|kemence}}, {{W+|hu|sütő}}
* {{Z|mn}}: {{W+|mn|зуух}}
* {{Z|nci}}: {{W-|nci|texcalli}}, {{W-|nci|tlaxcaltexcalli}}
* {{Z|nap}}: {{W-|nap|furno}}
* {{Z|nds}}:
*: {{Z|nds-de}}: {{W-|nds-de|Aven}}
* {{Z|ne}}: {{W-|ne|चुल्हो}}
* {{Z|nrf}}: {{W-|nrf|fou|n}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|ovn|n}}, {{W-|nb|stekeovn|n}}
*: {{Z|nn}}: {{W-|nn|omn|n}}
* {{Z|oc}}: {{W+|oc|forn|n}}
* {{Z|ota}}: {{W-|ota|فرون|tr=furun}}
* {{Z|ug}}: {{W-|ug|ئوچاق}}, {{W-|ug|دۇخوپكا}}, {{W-|ug|تونۇر}}
* {{Z|uz}}: {{W+|uz|pechka}}, {{W+|uz|pech}}, {{W+|uz|oʻchoq}}, {{W+|uz|duxovka}}
* {{Z|ps}}: {{W+|ps|تنور|n|tr=tanúr}}
* {{Z|pdt}}: {{W-|pdt|Owen|n}}
* {{Z|pox}}: {{W-|pox|pic|m}}
* {{Z|pl}}: {{W+|pl|piekarnik|n}}, {{W+|pl|piec|n}}
* {{Z|pt}}: {{W+|pt|forno|n}}
* {{Z|kk}}: {{W-|kk|пеш}}
* {{Z|rm}}: {{W-|rm|furn|n}}, {{W-|rm|fuorn|n}}, {{W-|rm|furnel|n}}, {{W-|rm|pigna|m}}, {{W-|rm|pegna|m}}
* {{Z|rom}}: {{W-|rom|bov|n}}
* {{Z|ro}}: {{W+|ro|cuptor|nt}}, {{W+|ro|sobă|m}}
* {{Z|ru}}: {{W+|ru|печь|m}}, {{W+|ru|пе́чка|m}}, {{W+|ru|духо́вка|m}}
* {{Z|rue}}: {{W-|rue|блóвдер|n}}
* {{Z|se}}: {{W-|se|omman}}
* {{Z|sc}}: {{W-|sc|forru|n}}, {{W-|sc|furru|n}}
* {{Z|scn}}: {{W-|scn|furneddu|n}}
* {{Z|szl}}: {{W-|szl|bratruła|m}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|пећ|m}}
*: Latînî: {{W+|sh|peć|m}}
* {{Z|sk}}: {{W-|sk|piecka|m}}, {{W-|sk|pec|m}}
* {{Z|sl}}: {{W+|sl|peč|m}}, {{W-|sl|pečica|m}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|pjac|n}}, {{W-|dsb|kamjeny|p}}
* {{Z|es}}: {{W+|es|horno|n}}
* {{Z|sv}}: {{W+|sv|ugn|g}}
* {{Z|tg}}: {{W+|tg|танӯр}}, {{W+|tg|печка}}
* {{Z|ta}}: {{W+|ta|அடுப்பு}}
* {{Z|rif}}: {{W-|rif|tafqunt|m}}
* {{Z|th}}: {{W+|th|เตาอบ}}
* {{Z|te}}: {{W+|te|పొయ్యి}}
* {{Z|crh}}: {{W-|crh|furun}}, {{W-|crh|pırım}}
* {{Z|tt}}: {{W-|tt|фурун}}
* {{Z|tr}}: {{W+|tr|fırın}}, {{W+|tr|ocak}}
* {{Z|tk}}: {{W-|tk|peç}}, {{W-|tk|ojak}}
* {{Z|udm}}: {{W-|udm|гур}}
* {{Z|uga}}: {{W-|uga|𐎚𐎐𐎐𐎗}}
* {{Z|ur}}: {{W-|ur|تندور|n|tr=tandūr}}
* {{Z|uk}}: {{W+|uk|піч|m}}, {{W-|uk|пі́чка|m}}
* {{Z|vep}}: {{W-|vep|päč}}
* {{Z|vi}}: {{W+|vi|lò}}, {{W-|vi|lò nướng}}
* {{Z|vo}}: {{W+|vo|furnod}}
* {{Z|wa}}: {{W+|wa|for|n}}
* {{Z|cy}}: {{W+|cy|ffwrn|m}}, {{W+|cy|popty|n}}
* {{Z|km}}: {{W+|km|ឡ}}
* {{Z|yi}}: {{W-|yi|אויוון}}
* {{Z|pnt}}: {{W-|pnt|φουρνίν|nt}}
* {{Z|el}}: {{W+|el|φούρνος|n}}
*: Kevn: {{W-|grc|ἰπνός|n}}, {{W-|grc|κάμινος|n|m}}, {{W-|grc|κλίβανος|n}}
* {{Z|za}}: {{W-|za|gaujluz}}
{{werger-bin}}
:{{werger çavkanî|en|oven|beş=Ev qismê Wergerê|oldid=76292557}}
{{werger-ser|'''dikana nanî dipêje û difiroşe'''|2}}
* {{Z|sq}}: {{W-|sq|dyqan buke}}
* Apaçî:
*: {{Z|apw}}: {{W-|apw|báń áldeʼyú}}
* {{Z|de}}: {{W+|de|Bäckerei|m}}, {{W+|de|Backstube|m}}
* {{Z|ast}}: {{W-|ast|panadería|m}}
* {{Z|az}}: {{W-|az|çörəkxana}}
* {{Z|eu}}: {{W+|eu|okindegi}}, {{W+|eu|ogitegi}}
* {{Z|be}}: {{W-|be|пяка́рня|m|sc=Cyrl}}
* {{Z|bn}}: {{W-|bn|বেকারি}}
* {{Z|bg}}: {{W-|bg|фу́рна|m|sc=Cyrl}}, {{W-|bg|пека́рна|m|sc=Cyrl}}, {{W-|bg|хлеба́рница|m|sc=Cyrl}}
* {{Z|my}}: {{W-|my|မုန့်ဖုတ်ရုံ|sc=Mymr}}
* {{Z|cs}}: {{W+|cs|pekárna|m}}, {{W+|cs|pekařství|nt}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W-|cmn|麵包店|tr=miànbāodiàn}}, {{W-|cmn|麵包房|tr=miànbāofáng}}
* {{Z|da}}: {{W+|da|bageri|nt}}
* {{Z|id}}: {{W-|id|toko roti}}, {{W-|id|bakeri}}
* {{Z|ar}}: {{W-|ar|مَخْبِز|n}}
*: {{Z|ary}}: {{W-|ary|مخبزة|tr=maḵbaza}}
* {{Z|hy}}: {{W+|hy|փուռ|sc=Armn}}
* {{Z|eo}}: {{W-|eo|bakejo}}
* {{Z|et}}: {{W-|et|pagaritööstus}}
* {{Z|fa}}: {{W+|fa|نانوائی|tr=nânvâ'i, nânevâ'i}}, {{W-|fa|دکان نانوائی|tr=dokân nânevâ'i}}
* {{Z|fi}}: {{W+|fi|leipomo}}
* {{Z|fr}}: {{W+|fr|boulangerie|m}}
* {{Z|gl}}: {{W+|gl|panadaría|m}}, {{W+|gl|panadería|m}}, {{W+|gl|forno|n}}, {{W+|gl|tafona|m}}
* {{Z|ka}}: {{W-|ka|ფურნე}}, {{W-|ka|საფუნთუშე}}
* {{Z|hi}}: {{W-|hi|बेकरी|m}}
* {{Z|nl}}: {{W+|nl|bakkerij|m}}
* {{Z|he}}: {{W+|he|מַאֲפִיָּה|m|tr=maafia}}
* {{Z|io}}: {{W+|io|bakerio}}
* {{Z|ang}}: {{W-|ang|bæchūs|nt}}, {{W-|ang|bæcern|nt}}
* {{Z|en}}: {{W+|en|bakery}}
* {{Z|it}}: {{W+|it|panetteria|m}}, {{W+|it|forno|n}}, {{W+|it|panificio|n}}
* {{Z|is}}: {{W-|is|bakarí|nt}}
* {{Z|ja}}: {{W+|ja|パン屋|tr=ぱんや, pan'ya}}, {{W+|ja|ベーカリー|tr=bēkarī|sc=Jpan}}
* {{Z|ca}}: {{W+|ca|fleca|m}}, {{W+|ca|forn|n}}
* {{Z|qu}}: {{W-|qu|t'anta ranqhana wasi}}
* {{Z|ky}}: {{W+|ky|наабайкана|sc=Cyrl}}
* {{Z|ko}}: {{W+|ko|빵집}}, {{W+|ko|제과점}}, {{W-|ko|베이커리}}
* {{Z|la}}: {{W-|la|pistrīna|m}}
* {{Z|lv}}: {{W-|lv|maiznīca|m}}
* {{Z|lo}}: {{W-|lo|ຮ້ານຂາຍເຂົ້າຈີ່}}
* {{Z|lt}}: {{W-|lt|kepykla|m}}
* {{Z|mk}}: {{W-|mk|пека́рница|m}}, {{W-|mk|фу́рна|m}}, {{W-|mk|леба́рница|m}}
* {{Z|ms}}: {{W-|ms|kedai roti}}
* {{Z|mt}}: {{W-|mt|forn}}
* {{Z|mi}}: {{W-|mi|hereumu parāoa}}, {{W-|mi|hereumu}}
* {{Z|hu}}: {{W+|hu|kenyérbolt}}, {{W+|hu|péküzlet}}, {{W+|hu|pékség}}
* {{Z|mn}}: {{W-|mn|талх барих газар|sc=Cyrl}}
* {{Z|nv}}: {{W-|nv|bááh álʼį́įgi}}
* {{Z|nrf}}: {{W-|nrf|boulangu'thie|m}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|bakeri|nt}}
* {{Z|ota}}: {{W-|ota|فرن|tr=fırın|sc=ota-Arab}}
* {{Z|uz}}: {{W+|uz|novvoyxona}}
* {{Z|pdt}}: {{W-|pdt|Bakjarie|m}}
* {{Z|pl}}: {{W+|pl|piekarnia|m}}
* {{Z|pt}}: {{W+|pt|padaria|m}}
* {{Z|kk}}: {{W-|kk|наубайхана|sc=Cyrl}}, {{W-|kk|нанхана|sc=Cyrl}}
* {{Z|ro}}: {{W+|ro|brutărie|m}}
* {{Z|ru}}: {{W+|ru|пека́рня|m}}, {{W+|ru|хлебопека́рня|m}}, {{W+|ru|бу́лочная|m}}
* {{Z|scn}}: {{W+|scn|furnu|n}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|пекара|m|sc=Cyrl}}, {{W-|sh|пекарница|m|sc=Cyrl}}
*: Latînî: {{W+|sh|pekara|m}}, {{W+|sh|pekarnica|m}}
* {{Z|sk}}: {{W+|sk|pekáreň|m}}
* {{Z|sl}}: {{W+|sl|pekarna|m}}
* {{Z|es}}: {{W+|es|panadería|m}}, {{W-|es|tahona|m}}, {{W+|es|horno|n}}
* {{Z|sv}}: {{W+|sv|bageri|nt}}
* {{Z|tg}}: {{W-|tg|нонвойхона}}, {{W-|tg|дӯкони хаб}}
* {{Z|tl}}: {{W-|tl|panaderya}}, {{W-|tl|tinapayan}}
* {{Z|th}}: {{W+|th|ร้านขนมปัง|tr=ráan kà-nǒm bpang}}, {{W+|th|เบเกอรี|tr=bee-gəə-rii}}
* {{Z|tr}}: {{W+|tr|fırın}}, {{W+|tr|pastane}}
* {{Z|tk}}: {{W-|tk|çörekhana}}
* {{Z|ur}}: {{W-|ur|بیکری|m|tr=bekrī|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|пека́рня|m|sc=Cyrl}}
* {{Z|vi}}: {{W-|vi|lò bánh mì}}, {{W-|vi|tiệm bánh}}
* {{Z|vo}}: {{W+|vo|baköp}}
* {{Z|wa}}: {{W+|wa|boledjreye|m}}
* {{Z|cy}}: {{W-|cy|siop fara|m}}, {{W-|cy|becws|n}}
* {{Z|km}}: {{W-|km|ឡដុតនំប៉័ង|tr=lɑɑ dot num paŋ|sc=Khmr}}
* {{Z|yi}}: {{W-|yi|בעקערײַ|m|sc=Hebr}}
* {{Z|el}}: {{W+|el|φούρνος|n}}, {{W+|el|αρτοποιείο|nt|sc=Grek}}
*: Kevn: {{W-|grc|ἀρτοπώλιον|nt}}
{{werger-bin}}
:{{werger çavkanî|en|bakery|beş=Ev qismê Wergerê|oldid=76469064}}
qihwievw2n57w46mzzq72d5dfeh9x7l
gewr
0
25831
6276938
6262689
2026-07-23T13:22:03Z
Memo1341
14691
/* Etîmolojî */ Kilîma "gewre" ji zimanê aramî hate deynkirin, û hevreha kilîma "gewr" nîne.
6276938
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163 (kmr)-Key Mîrza-gewr.wav|Deng|Wan}}
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# {{f|ku|reng}} [[reng|Reng]]ê [[mîna]] yê [[gur]]an; [[reng]]ek [[navber]]a [[reş]]î û [[spî|spiyî]] ye.
#: Yek ji van rengan: {{reng|#444444}}{{reng|#666666}}{{reng|#888888}}{{reng|#AAAAAA}}{{reng|#CCCCCC}}
#: {{hevmane|ku|boz|grî|bor}}
#* {{bêmînak|ku}}
# {{f|ku|Reşwî}} Rengê [[spî]] yan rengên wek spî.
#* {{bêmînak|ku}}
==== Formên alternatîv ====
{{kol3|ku
|gewer
|gewir
}}
==== Bide ber ====
{{kol3|ku
|gewer
|gewre
}}
==== Jê ====
{{kol3|ku|cure=Jê
|gewrahî
|gewratî
|gewrayî
|gewrî<q:2>
|gewrîtî
|gewirtî
|gewr bûn<pos:lêker>
|gewr kirin<pos:lêker>
|gewrbûn<pos:navdêr>
|gewrkirin<pos:navdêr>
|gewrbûyî<pos:rengdêr>
|gewrkirî<pos:rengdêr>
}}
==== Etîmolojî ====
Ji {{mîr|ine-pro|ku|*gʰrewos}}, bide ber almanî [[grau]], îngilîzî [[grey]] / [[gray]], fransî [[gris]], ermenî [[գորշ]] (gorş)... Ne ji heman rehî: [[gewre]].
==== Bi zaravayên kurdî ====
* Soranî: {{W-|ckb|گەور}}, {{W-|ckb|خۆڵەمێشی}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|grys}}, {{W+|af|aangeskote}}, {{W+|af|beneweld}}
* {{Z|de}}: {{W+|de|grau}}, {{W+|de|angeheitert}}, {{W+|de|beschwipst}}, {{W+|de|gräulich}}, {{W+|de|weißgrau}}
* {{Z|cs}}: {{W+|cs|šedivý}}
* {{Z|da}}: {{W+|da|grå}}
* {{Z|eo}}: {{W+|eo|griza}}, {{W+|eo|ebrieta}}, {{W+|eo|duonebria}}
* {{Z|fa}}: {{W+|fa|خاکستری}}, {{W+|fa|خاکستری}}, {{W+|fa|خاکستری}}
* {{Z|fo}}: {{W+|fo|gráur}}
* {{Z|fi}}: {{W+|fi|harmaa}}
* {{Z|fr}}: {{W+|fr|gris}}
* {{Z|fur}}: {{W+|fur|grau}}, {{W+|fur|griis}}, {{W+|fur|skier}}, {{W+|fur|oansketten}}
* {{Z|fy}}: {{W+|fy|grau}}, {{W+|fy|griis}}, {{W+|fy|skier}}, {{W+|fy|oansketten}}
* {{Z|gd}}: {{W+|gd|glas}}
* {{Z|nl}}: {{W+|nl|grijs}}, {{W+|nl|grauw}}, {{W+|nl|aangeschoten}}, {{W+|nl|beneveld}}, {{W+|nl|tipsy}}, {{W+|nl|roezig}}
* {{Z|io}}: {{W+|io|griza}}
* {{Z|ang}}: {{W+|ang|græg}}
* {{Z|en}}: {{W+|en|grey}}, {{W+|en|tipsy}}, {{W+|en|gray}}, {{W+|en|greyish}}, {{W+|en|griseous}}, {{W+|en|grizzled}}, {{W+|en|hoar}}, {{W|en|cinerous|guman=erê}}, {{W|en|slaty|guman=erê}}
* {{Z|it}}: {{W+|it|bigio}}, {{W+|it|grigio}}
* {{Z|is}}: {{W+|is|grár}}
* {{Z|ca}}: {{W+|ca|gris}}
* {{Z|la}}: {{W+|la|canens}}, {{W+|la|canus}}, {{W+|la|ravus}}
* {{Z|li}}: {{W+|li|gries}}
* {{Z|hu}}: {{W+|hu|szürke}}
* {{Z|no}}: {{W+|no|grå}}
* {{Z|pap}}: {{W+|pap|shinishi}}
* {{Z|pl}}: {{W+|pl|szary}}, {{W+|pl|siwy}}
* {{Z|pt}}: {{W+|pt|cinza}}, {{W+|pt|cinzento}}, {{W+|pt|pardo}}
* {{Z|ro}}: {{W+|ro|gri}}
* {{Z|es}}: {{W+|es|gris}}, {{W+|es|achispado}}
* {{Z|sv}}: {{W+|sv|grå}}
* {{Z|tl}}: {{W+|tl|kúlay-abó}}
* {{Z|tr}}: {{W+|tr|gri}}, {{W+|tr|boz}}, {{W+|tr|demiri}}, {{W+|tr|kır}}, {{W+|tr|duman rengi|guman=erê}}, {{W|tr|kumral|guman=erê}}, {{W|tr|kül rengi|guman=erê}}
{{werger-bin}}
=== Navdêr 1 ===
{{navdêr|ku|z=n}}
# Her tiştê ku ew reng heye.
#* {{bêmînak|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W+|en|grey}}
{{werger-bin}}
=== Navdêr 2 ===
{{navdêr|ku|z=mn}}
# {{guharto|ku|gawir|w=kesê bawerî bi ti dînê neanîye; kafir, zordar}}
#* {{bêmînak|ku}}
s1dl5q8c0c4hbo5v6s4tfbsceb8ml56
neşir
0
32908
6276940
6263498
2026-07-23T16:54:55Z
Wikihez
7344
/* Navdêr 1 */
6276940
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Navdêr 1 ===
{{ku-tewîn-nav|neşir|mê}}
{{navdêr|ku|z=m}}
# [[weşan]], [[belavok]], [[belavkirin]]
#* {{bêmînak|ku}}
==== Formên alternatîv ====
* {{g|ku|neşr}}
==== Jê ====
{{stûn|
* {{mj|lêker}} {{g|ku|neşir bûn}}
* {{mj|lêker}} {{g|ku|neşir kirin}}
* {{mj|navdêr}} {{g|ku|neşirbûn}}
* {{mj|navdêr}} {{g|ku|neşirkirin}}
* {{mj|rengdêr}} {{g|ku|neşirbûyî}}
* {{mj|rengdêr}} {{g|ku|neşirkirî}}
* {{g|ku|neşirker}}
}}
==== Etîmolojî ====
Ji {{ji|ar|ku|نشر|tr=neşr|w=[[belav]]kirin, [[çap]]kirin}} ji {{g|ar|ن ش ر|tr=n-ş-r-|w=belav bûn, [[pelate]] bûn}}, hevreha {{hv|arc|נשׁר|tr=n-ş-r-|w=[[belav]] bûn, [[pelate]] bûn}} .
==== Werger ====
{{werger-ser}}
* {{Z|fa}}: {{W+|fa|نثر}}, {{W+|fa|چاپ}}, {{W+|fa|نشر}}
* {{Z|tr}}: {{W+|tr|neşir}}, {{W+|tr|yayım}}
{{werger-bin}}
=== Navdêr 2 ===
{{navdêr|ku|z=m}}
# {{guherto|ku|heşir}}
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# [[weşan]], [[weşanek]], [[pexş]], [[neşr]]
#* {{bêmînak|tr}}
# [[belavkirin]], [[reşandin]]
#* {{bêmînak|tr}}
rdsbkzkrhfze85yd1m8yyicwv7tzo3p
6276941
6276940
2026-07-23T16:55:14Z
Wikihez
7344
/* Jê */
6276941
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Navdêr 1 ===
{{ku-tewîn-nav|neşir|mê}}
{{navdêr|ku|z=m}}
# [[weşan]], [[belavok]], [[belavkirin]]
#* {{bêmînak|ku}}
==== Formên alternatîv ====
* {{g|ku|neşr}}
==== Jê ====
{{kol3|ku|cure=Jê
|neşir bûn<q:lêker>
|neşir kirin<q:lêker>
|neşirbûn<q:navdêr>
|neşirkirin<q:navdêr>
|neşirbûyî<q:rengdêr>
|neşirkirî<q:rengdêr>
|neşirker
}}
==== Etîmolojî ====
Ji {{ji|ar|ku|نشر|tr=neşr|w=[[belav]]kirin, [[çap]]kirin}} ji {{g|ar|ن ش ر|tr=n-ş-r-|w=belav bûn, [[pelate]] bûn}}, hevreha {{hv|arc|נשׁר|tr=n-ş-r-|w=[[belav]] bûn, [[pelate]] bûn}} .
==== Werger ====
{{werger-ser}}
* {{Z|fa}}: {{W+|fa|نثر}}, {{W+|fa|چاپ}}, {{W+|fa|نشر}}
* {{Z|tr}}: {{W+|tr|neşir}}, {{W+|tr|yayım}}
{{werger-bin}}
=== Navdêr 2 ===
{{navdêr|ku|z=m}}
# {{guherto|ku|heşir}}
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# [[weşan]], [[weşanek]], [[pexş]], [[neşr]]
#* {{bêmînak|tr}}
# [[belavkirin]], [[reşandin]]
#* {{bêmînak|tr}}
i1gx7e0hygyut55ovgul69ugg6t2e80
binefş
0
46917
6276927
6276926
2026-07-23T12:06:27Z
Wikihez
7344
[[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6276927
wikitext
text/x-wiki
{{bnr-h|Binefş}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-binefş.wav|Deng|Amed}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|binefş|mê}}
[[Wêne:Purple African Violet Top.JPG|thumb|Binefş]]
{{navdêr|ku|z=m}}
# [[gul|Gul]]ek [[rengîn]] e û bi [[gelemper]]î rengê [[kulîlk]]ên wê [[mor]] anku [[binefşî]] ne.
#* {{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî|jêgirtin=Çimen da lale bişkiftin<br />'''Binefş''' û çîçekan vêk ra<br />Şefeq da yek di rewdê da}}
==== Formên alternatîv ====
* {{g|ku|benefş}}
* {{g|ku|benevş}}
* {{g|ku|binevş}}
* {{g|ku|binewş}}
==== Bi alfabeyên din ====
* {{ku-ar|بنهفش}}
==== Jê ====
* {{g|ku|binefşî}}
* {{g|ku|binefşîtî}}
* {{g|ku|binefa belek}}
* {{g|ku|Binefşa Narîn|t=[[evîndar]]a [[Cembelî]] di [[dastan]]a [[Cembeliyê Hekarî]] de}}
==== Bi zaravayên din ====
* {{Z|hac}}: {{W-|hac|wenewşe|sc=Latn}}
* {{Z|ckb}}: {{W-|ckb|بنەوشە|tr=binewşe}} {{W-|ckb|benewşe|sc=Latn}}
* {{Z|zza}}: {{W-|zza|binefşe}} {{W-|zza|menewşe}}
==== Etîmolojî ====
Hevreha {{hevreh|ckb|بنەوشە|tr=binewşe}}, {{hv|hac|wenewşe|sc=Latn}}, {{hv|fa|بنفشه|tr=benefşê}}, {{hv|pal||venevşek}}, {{hv|ae||*vene-vexşe-|sc=Latn}} ji ''vexşe-'' ([[şîn]] bûn, [[gihiştin]]) ji {{ji|ine-pro|ku|*h₂ewg-|tr=şîn bûn}} ku herwiha serekaniya [[աճեմ]] (açêm) ya ermeniya kevn, [[αὐξάνω]] (aûksano) ya yûnaniya kevn, [[ageo]] ya {{hv|la|wax}} ya îngilîzî [[öka]] ya swêdî, [[áugti]] ya lîtwanî ye. {{g|fa|بنفسج|tr=benefsec}} ya erebî, [[menekşe]] ya tirkî, [[մանիշակ]] (menişek) ya ermenî û [[ܡܢܝܫܟܐ]] (minîşka) ya siryanî ji zimanên îranî hatine wergirtin. Heman peyva îranî ''vexş-'' (şîn bûn, gihiştin) serekaniya peyva [[wehş]] e ku bi rêya erebî û bi kirasê erebî li zimanên îranî vegeriye.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Veilchen|n}}
* {{Z|zh}}: {{W+|zh|紫羅蘭}}, [[紫罗兰]] (zǐluólán)
* {{Z|ar}}: {{W+|ar|بنفسج|guman=erê}}
* {{Z|fa}}: {{W-|fa|بنفش|tr=benefş}}, {{W+|fa|بنفش}}
* {{Z|fi}}: {{W+|fi|orvokki}}
* {{Z|nl}}: {{W+|nl|viool}}, {{W+|nl|viooltje|f}}, {{W+|nl|violet}}
* {{Z|he}}: {{W-|he|סגל|tr=Sigahl}}
* {{Z|ang}}: {{W-|ang|æppelleaf}}
* {{Z|en}}: {{W-|en|violet|tr=the plant}}, {{W+|en|purpureo-}}, {{W+|en|violently}}, {{W+|en|picotee|guman=erê}}
* {{Z|it}}: {{W-|it|viola#Italian|f}}
* {{Z|ja}}: {{W-|ja|菫|tr=sumire}}
* {{Z|hu}}: {{W+|hu|ibolya}}
* {{Z|pl}}: {{W+|pl|fiołek|m}}
* {{Z|pt}}: {{W+|pt|violeta|f}}
* {{Z|ru}}: {{W-|ru|фиалка|tr=fiálka}}
* {{Z|sh}}: {{W-|sh|ljubičica#Bosnian|f}}
* {{Z|sh}}:
*: Kirîlî: [[љубичица#Serbian|љубичица]] {{m}}
*: Latînî: {{W-|la|ljubičica#Serbian|f}}
* {{Z|es}}: {{W+|es|violeta|f}}
* {{Z|sv}}: {{W+|sv|viol|c}}
* {{Z|tr}}: {{W+|tr|menekşe}}
{{werger-bin}}
{{mijar|ku|Kulîlk}}
bbqpht374yy8fr2aox0wpryuhfqf5ki
6276934
6276927
2026-07-23T12:41:00Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6276934
wikitext
text/x-wiki
{{bnr-h|Binefş}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-binefş.wav|Deng|Amed}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|binefş|mê}}
[[Wêne:Purple African Violet Top.JPG|thumb|Binefş]]
{{navdêr|ku|z=m}}
# [[gul|Gul]]ek [[rengîn]] e û bi [[gelemper]]î rengê [[kulîlk]]ên wê [[mor]] anku [[binefşî]] ne.
#* {{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî|jêgirtin=Çimen da lale bişkiftin<br />'''Binefş''' û çîçekan vêk ra<br />Şefeq da yek di rewdê da}}
==== Formên alternatîv ====
* {{g|ku|benefş}}
* {{g|ku|benevş}}
* {{g|ku|binevş}}
* {{g|ku|binewş}}
==== Bi alfabeyên din ====
* {{ku-ar|بنهفش}}
==== Jê ====
* {{g|ku|binefşî}}
* {{g|ku|binefşîtî}}
* {{g|ku|binefa belek}}
* {{g|ku|Binefş|Binefşa Narîn|t=[[evîndar]]a [[Cembelî]] di [[dastan]]a [[Cembeliyê Hekarî]] de}}
==== Bi zaravayên din ====
* {{Z|hac}}: {{W-|hac|wenewşe|sc=Latn}}
* {{Z|ckb}}: {{W-|ckb|بنەوشە|tr=binewşe}} {{W-|ckb|benewşe|sc=Latn}}
* {{Z|zza}}: {{W-|zza|binefşe}} {{W-|zza|menewşe}}
==== Etîmolojî ====
Hevreha {{hevreh|ckb|بنەوشە|tr=binewşe}}, {{hv|hac|wenewşe|sc=Latn}}, {{hv|fa|بنفشه|tr=benefşê}}, {{hv|pal||venevşek}}, {{hv|ae||*vene-vexşe-|sc=Latn}} ji ''vexşe-'' ([[şîn]] bûn, [[gihiştin]]) ji {{ji|ine-pro|ku|*h₂ewg-|tr=şîn bûn}} ku herwiha serekaniya [[աճեմ]] (açêm) ya ermeniya kevn, [[αὐξάνω]] (aûksano) ya yûnaniya kevn, [[ageo]] ya {{hv|la|wax}} ya îngilîzî [[öka]] ya swêdî, [[áugti]] ya lîtwanî ye. {{g|fa|بنفسج|tr=benefsec}} ya erebî, [[menekşe]] ya tirkî, [[մանիշակ]] (menişek) ya ermenî û [[ܡܢܝܫܟܐ]] (minîşka) ya siryanî ji zimanên îranî hatine wergirtin. Heman peyva îranî ''vexş-'' (şîn bûn, gihiştin) serekaniya peyva [[wehş]] e ku bi rêya erebî û bi kirasê erebî li zimanên îranî vegeriye.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Veilchen|n}}
* {{Z|zh}}: {{W+|zh|紫羅蘭}}, [[紫罗兰]] (zǐluólán)
* {{Z|ar}}: {{W+|ar|بنفسج|guman=erê}}
* {{Z|fa}}: {{W-|fa|بنفش|tr=benefş}}, {{W+|fa|بنفش}}
* {{Z|fi}}: {{W+|fi|orvokki}}
* {{Z|nl}}: {{W+|nl|viool}}, {{W+|nl|viooltje|f}}, {{W+|nl|violet}}
* {{Z|he}}: {{W-|he|סגל|tr=Sigahl}}
* {{Z|ang}}: {{W-|ang|æppelleaf}}
* {{Z|en}}: {{W-|en|violet|tr=the plant}}, {{W+|en|purpureo-}}, {{W+|en|violently}}, {{W+|en|picotee|guman=erê}}
* {{Z|it}}: {{W-|it|viola#Italian|f}}
* {{Z|ja}}: {{W-|ja|菫|tr=sumire}}
* {{Z|hu}}: {{W+|hu|ibolya}}
* {{Z|pl}}: {{W+|pl|fiołek|m}}
* {{Z|pt}}: {{W+|pt|violeta|f}}
* {{Z|ru}}: {{W-|ru|фиалка|tr=fiálka}}
* {{Z|sh}}: {{W-|sh|ljubičica#Bosnian|f}}
* {{Z|sh}}:
*: Kirîlî: [[љубичица#Serbian|љубичица]] {{m}}
*: Latînî: {{W-|la|ljubičica#Serbian|f}}
* {{Z|es}}: {{W+|es|violeta|f}}
* {{Z|sv}}: {{W+|sv|viol|c}}
* {{Z|tr}}: {{W+|tr|menekşe}}
{{werger-bin}}
{{mijar|ku|Kulîlk}}
8zaxhbvidu0ytmj9vnpa6h6mhoa42m2
6276935
6276934
2026-07-23T12:47:46Z
Wikihez
7344
6276935
wikitext
text/x-wiki
{{bnr-h|Binefş}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-binefş.wav|Deng|Amed}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|binefş|mê}}
[[Wêne:Purple African Violet Top.JPG|thumb|Binefş]]
{{navdêr|ku|z=m}}
# [[gul|Gul]]ek [[rengîn]] e û bi [[gelemper]]î rengê [[kulîlk]]ên wê [[mor]] anku [[binefşî]] ne.
#* {{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî|jêgirtin=Çimen da lale bişkiftin<br />'''Binefş''' û çîçekan vêk ra<br />Şefeq da yek di rewdê da}}
==== Bi alfabeyên din ====
* {{ku-ar|بنهفش}}
==== Formên alternatîv ====
* {{g|ku|benefş}}
* {{g|ku|benevş}}
* {{g|ku|binevş}}
* {{g|ku|binewş}}
==== Jê ====
* {{g|ku|binefşî}}
* {{g|ku|binefşîtî}}
* {{g|ku|binefa belek}}
* {{g|ku|Binefş|Binefşa Narîn|t=[[evîndar]]a [[Cembelî]] di [[dastan]]a [[Cembeliyê Hekarî]] de}}
==== Etîmolojî ====
Hevreha {{hevreh|ckb|بنەوشە|tr=binewşe}}, {{hv|hac|wenewşe|sc=Latn}}, {{hv|fa|بنفشه|tr=benefşê}}, {{hv|pal||venevşek}}, {{hv|ae||*vene-vexşe-|sc=Latn}} ji ''vexşe-'' ([[şîn]] bûn, [[gihiştin]]) ji {{ji|ine-pro|ku|*h₂ewg-|tr=şîn bûn}} ku herwiha serekaniya [[աճեմ]] (açêm) ya ermeniya kevn, [[αὐξάνω]] (aûksano) ya yûnaniya kevn, [[ageo]] ya {{hv|la|wax}} ya îngilîzî [[öka]] ya swêdî, [[áugti]] ya lîtwanî ye. {{g|fa|بنفسج|tr=benefsec}} ya erebî, [[menekşe]] ya tirkî, [[մանիշակ]] (menişek) ya ermenî û [[ܡܢܝܫܟܐ]] (minîşka) ya siryanî ji zimanên îranî hatine wergirtin. Heman peyva îranî ''vexş-'' (şîn bûn, gihiştin) serekaniya peyva [[wehş]] e ku bi rêya erebî û bi kirasê erebî li zimanên îranî vegeriye.
==== Bi zaravayên din ====
* {{Z|hac}}: {{W-|hac|wenewşe|sc=Latn}}
* {{Z|ckb}}: {{W-|ckb|بنەوشە|tr=binewşe}} {{W-|ckb|benewşe|sc=Latn}}
* {{Z|zza}}: {{W-|zza|binefşe}} {{W-|zza|menewşe}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Veilchen|n}}
* {{Z|zh}}: {{W+|zh|紫羅蘭}}, [[紫罗兰]] (zǐluólán)
* {{Z|ar}}: {{W+|ar|بنفسج|guman=erê}}
* {{Z|fa}}: {{W-|fa|بنفش|tr=benefş}}, {{W+|fa|بنفش}}
* {{Z|fi}}: {{W+|fi|orvokki}}
* {{Z|nl}}: {{W+|nl|viool}}, {{W+|nl|viooltje|f}}, {{W+|nl|violet}}
* {{Z|he}}: {{W-|he|סגל|tr=Sigahl}}
* {{Z|ang}}: {{W-|ang|æppelleaf}}
* {{Z|en}}: {{W-|en|violet|tr=the plant}}, {{W+|en|purpureo-}}, {{W+|en|violently}}, {{W+|en|picotee|guman=erê}}
* {{Z|it}}: {{W-|it|viola#Italian|f}}
* {{Z|ja}}: {{W-|ja|菫|tr=sumire}}
* {{Z|hu}}: {{W+|hu|ibolya}}
* {{Z|pl}}: {{W+|pl|fiołek|m}}
* {{Z|pt}}: {{W+|pt|violeta|f}}
* {{Z|ru}}: {{W-|ru|фиалка|tr=fiálka}}
* {{Z|sh}}: {{W-|sh|ljubičica#Bosnian|f}}
* {{Z|sh}}:
*: Kirîlî: [[љубичица#Serbian|љубичица]] {{m}}
*: Latînî: {{W-|la|ljubičica#Serbian|f}}
* {{Z|es}}: {{W+|es|violeta|f}}
* {{Z|sv}}: {{W+|sv|viol|c}}
* {{Z|tr}}: {{W+|tr|menekşe}}
{{werger-bin}}
{{mijar|ku|Kulîlk}}
s58ms677ep5fq5cjyopta3euikw5jn3
güzelce
0
139777
6276946
6076175
2026-07-23T18:33:06Z
Peyvkar
14865
/* Rengdêr */ Rengdêrek lê zêde kir
6276946
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Rengdêr ===
{{rengdêr|tr}}
# [[rindikî]], [[xweşikî]], [pakepak]], bi xweşikî ve
#: {{mk|tr|'''Güzelce''' bir kız.|Keçeke bi xweşikî ve.|rêz=1}}
# têr xweşik, têr baş
#: {{mk|tr|Kadın etrafı '''güzelce''' süpürmüştü.|Jinik derdor têr xweşik ma laştibû.|rêz=1}}
rvjqy1y92astkta0vd2zleqpaiej4op
6276947
6276946
2026-07-23T18:34:00Z
Peyvkar
14865
Sererastkirin
6276947
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Rengdêr ===
{{rengdêr|tr}}
# [[rindikî]], [[xweşikî]], [[pakepak]], bi xweşikî ve
#: {{mk|tr|'''Güzelce''' bir kız.|Keçeke bi xweşikî ve.|rêz=1}}
# têr xweşik, têr baş
#: {{mk|tr|Kadın etrafı '''güzelce''' süpürmüştü.|Jinik derdor têr xweşik ma laştibû.|rêz=1}}
i6wa163rho3pepihaqubrcrt1yrwv3x
6276955
6276947
2026-07-23T19:18:48Z
Wikihez
7344
Guhartinên [[Special:Contributions/Peyvkar|Peyvkar]] ([[User talk:Peyvkar|gotûbêj]]) hatine betalkirin, vegerand guhartoya dawî ya [[User:Balyozbot|Balyozbot]]
6076175
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Rengdêr ===
{{rengdêr|tr}}
# [[rindikî]], [[xweşikî]], bi xweşikî ve
#: {{mk|tr|'''Güzelce''' bir kız.|Keçeke bi xweşikî ve.|rêz=1}}
# têr xweşik, têr baş
#: {{mk|tr|Kadın etrafı '''güzelce''' süpürmüştü.|Jinik derdor têr xweşik ma laştibû.|rêz=1}}
m8o9lq8vc1io6pz9pg8zhsr7s6swzvf
telefon etmek
0
166840
6277063
5792872
2026-07-24T04:55:35Z
Peyvkar
14865
/* Lêker */ Lêker lê zêde kirin
6277063
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Bilêvkirin ===
* {{deng|tr|LL-Q256 (tur)-Veravi95-telefon etmek.wav|Deng|dever=}}
=== Lêker ===
{{lêker|tr}}
# [[têl kirin]], [[li têlê xistin]], [[telefon kirin]], [[ba kirin]], [[têlefon jê ra vekirin]]
nlai0isibvjtwxrx1vd6i2sugiz7ata
têr xwarin
0
354458
6277029
6126088
2026-07-24T01:29:54Z
Ghybu
1393
/* Lêker */
6277029
wikitext
text/x-wiki
{{bnr-n|têrxwarin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Lêker ===
{{ku-tewîn-lk|xwarin|form=gerguhêz|niha=xw|borî=xwar|pêşgir=têr|valahî=erê}}
{{lêker|ku|c=h-gh}}
# {{bêmane|ku}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê firnê, qenc sor kirin û anîn ji Beyrim re. Beyrim jê '''têr xwar''', go : « Çima tu naxwî ? » Go : « Bavo, ez ji goşt aciz bûme, şevê din, min gelik xwarîye. |nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Bêrût|sal=1942|rûpel=166}}
==== Etîmolojî ====
Ji {{heve|ku|têr|xwarin}}.
==== Werger ====
{{werger-ser}}
* {{Z|fr}}: {{W+|fr|rassasier}}
* {{Z|tr}}: {{W+|tr|doymak}}, {{W+|tr|doyunmak}}
{{werger-bin}}
r5sfq2jqyf87rhcxkdy6mn39lkpl86h
6277032
6277029
2026-07-24T01:31:29Z
Ghybu
1393
/* Lêker */
6277032
wikitext
text/x-wiki
{{bnr-n|têrxwarin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Lêker ===
{{ku-tewîn-lk|xwarin|form=gerguhêz|niha=xw|borî=xwar|pêşgir=têr|valahî=erê}}
{{lêker|ku|c=h-gh}}
# {{bêmane|ku}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê firnê, qenc sor kirin û anîn ji Beyrim re. Beyrim jê '''têr xwar''', go : « Çima tu naxwî ? » Go : « Bavo, ez ji goşt aciz bûme, şevê din, min gelik xwarîye. |nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Parîs|sal=1940|rûpel=166}}
==== Etîmolojî ====
Ji {{heve|ku|têr|xwarin}}.
==== Werger ====
{{werger-ser}}
* {{Z|fr}}: {{W+|fr|rassasier}}
* {{Z|tr}}: {{W+|tr|doymak}}, {{W+|tr|doyunmak}}
{{werger-bin}}
h621cqriu0894q01v3jcmikjv374hhz
6277054
6277032
2026-07-24T02:23:26Z
Ghybu
1393
/* Lêker */
6277054
wikitext
text/x-wiki
{{bnr-n|têrxwarin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Lêker ===
{{ku-tewîn-lk|xwarin|form=gerguhêz|niha=xw|borî=xwar|pêşgir=têr|valahî=erê}}
{{lêker|ku|c=h-gh}}
# {{bêmane|ku}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê firnê, qenc sor kirin û anîn ji Beyrim re. Beyrim jê '''têr xwar''', go : « Çima tu naxwî ? » Go : « Bavo, ez ji goşt aciz bûme, şevê din, min gelik xwarîye. |nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Parîs|sal=1940|rûpel=166}}
===== Binêre herwiha ====
* {{g|ku|têr bûn}}
==== Etîmolojî ====
Ji {{heve|ku|têr|xwarin}}.
==== Werger ====
{{werger-ser}}
* {{Z|fr}}: {{W+|fr|rassasier}}
* {{Z|tr}}: {{W+|tr|doymak}}, {{W+|tr|doyunmak}}
{{werger-bin}}
ej9ju0lbvraxiswakc8srsahsf0say4
6277055
6277054
2026-07-24T02:23:40Z
Ghybu
1393
/* = Binêre herwiha */
6277055
wikitext
text/x-wiki
{{bnr-n|têrxwarin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Lêker ===
{{ku-tewîn-lk|xwarin|form=gerguhêz|niha=xw|borî=xwar|pêşgir=têr|valahî=erê}}
{{lêker|ku|c=h-gh}}
# {{bêmane|ku}}
#* {{jêder-pirtûk|ku|jêgirtin=Wê gavê, çûn ji Beyrim re berxek şerjêkirin û ji birîncî, ji sinûbera û ji dûvê wî hûr kirin li nav hev du û heşandin, avêtin hindurê firnê, qenc sor kirin û anîn ji Beyrim re. Beyrim jê '''têr xwar''', go : « Çima tu naxwî ? » Go : « Bavo, ez ji goşt aciz bûme, şevê din, min gelik xwarîye. |nivîskar=R. Lescot|sernav=Textes kurdes I. Contes, proverbes et énigmes (BEYRIM EL HEC)|cih=Parîs|sal=1940|rûpel=166}}
==== Binêre herwiha ====
* {{g|ku|têr bûn}}
==== Etîmolojî ====
Ji {{heve|ku|têr|xwarin}}.
==== Werger ====
{{werger-ser}}
* {{Z|fr}}: {{W+|fr|rassasier}}
* {{Z|tr}}: {{W+|tr|doymak}}, {{W+|tr|doyunmak}}
{{werger-bin}}
axbow7z4filjd0zvj29iq2iwpe08y5n
şerjêkirin
0
516749
6277038
5681310
2026-07-24T01:33:38Z
Ghybu
1393
/* Navdêr */
6277038
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{guharto|ku|serjêkirin}}
et5a960f5gbcsw0bqylnq2ln7ewv5cg
6277040
6277038
2026-07-24T01:34:30Z
Ghybu
1393
6277040
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Navdêr ===
{{navdêr|ku|z=m|lk=şer jê kirin}}
# {{guharto|ku|serjêkirin}}
m2z7aipcun2tljke6nxaq4n6lpduot3
Gotûbêja modulê:ku-tewîn-nav
829
652579
6276942
6235764
2026-07-23T17:06:49Z
Wikihez
7344
/* neşir, heşir */ beşeke nû
6276942
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
o2ujliqz76tuec8cqfm6j6rce9o1ysl
6276972
6276942
2026-07-23T20:43:55Z
Wikihez
7344
/* neşir, heşir */ Bersiv
6276972
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
n8rqk3ezj2qzszduy91t6n5xdw0x1c1
6276996
6276972
2026-07-23T23:33:42Z
Ghybu
1393
/* neşir, heşir */ Bersiv
6276996
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
1qd6e95i844hdrpgfwg6ivz8a0svwgm
6277003
6276996
2026-07-23T23:54:08Z
Wikihez
7344
/* neşir, heşir */ Bersiv
6277003
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
:::Erê parametre hewce ye min got belkî otomatîk dibe feqat na min îsmên bi -ir xilas dibin nêrî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/ceribandin&oldid=6277002] hemû nabin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:54, 23 tîrmeh 2026 (UTC)
sijefptzwhsy4yzafrzzzxb1b4onk5v
6277005
6277003
2026-07-23T23:55:37Z
Wikihez
7344
/* neşir, heşir */ Bersiv
6277005
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
:::Erê parametre hewce ye min got belkî otomatîk dibe feqat na min îsmên bi -ir xilas dibin nêrî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/ceribandin&oldid=6277002] hemû nabin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:54, 23 tîrmeh 2026 (UTC)
::::Gelo tenê kelîmeyên zimanên din de wisa dibe? Kes "agrê" dibêje li şûna "agirê"? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 23 tîrmeh 2026 (UTC)
5il7hrdkgrkli47yi8zsrg4fwdd5cyh
6277006
6277005
2026-07-24T00:03:05Z
Ghybu
1393
/* neşir, heşir */ Bersiv
6277006
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
:::Erê parametre hewce ye min got belkî otomatîk dibe feqat na min îsmên bi -ir xilas dibin nêrî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/ceribandin&oldid=6277002] hemû nabin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:54, 23 tîrmeh 2026 (UTC)
::::Gelo tenê kelîmeyên zimanên din de wisa dibe? Kes "agrê" dibêje li şûna "agirê"? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 23 tîrmeh 2026 (UTC)
:::::Li vira peyvên "fikir", "nesir", "heşir" "şehir" hemû bi vê format in: ''bêdeng'' (e, i) + ''dengdêr'' (s, ş, h, k) + -ir. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:03, 24 tîrmeh 2026 (UTC)
a4t6vqho6jm5lkzulg834go9uslnv42
6277007
6277006
2026-07-24T00:05:08Z
Ghybu
1393
/* neşir, heşir */
6277007
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
:::Erê parametre hewce ye min got belkî otomatîk dibe feqat na min îsmên bi -ir xilas dibin nêrî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/ceribandin&oldid=6277002] hemû nabin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:54, 23 tîrmeh 2026 (UTC)
::::Gelo tenê kelîmeyên zimanên din de wisa dibe? Kes "agrê" dibêje li şûna "agirê"? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 23 tîrmeh 2026 (UTC)
:::::Li vira peyvên "fikir", "nesir", "heşir", "şehir", "agir" hemû bi vê format in: ''bêdeng'' (e, i, a) + ''dengdêr'' (s, ş, h, k, g) + -ir. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:03, 24 tîrmeh 2026 (UTC)
7yfruzcmtc613xf12bihwy5gmazhmim
6277008
6277007
2026-07-24T00:05:26Z
Wikihez
7344
/* neşir, heşir */ Bersiv
6277008
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
:::Erê parametre hewce ye min got belkî otomatîk dibe feqat na min îsmên bi -ir xilas dibin nêrî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/ceribandin&oldid=6277002] hemû nabin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:54, 23 tîrmeh 2026 (UTC)
::::Gelo tenê kelîmeyên zimanên din de wisa dibe? Kes "agrê" dibêje li şûna "agirê"? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 23 tîrmeh 2026 (UTC)
:::::Li vira peyvên "fikir", "nesir", "heşir", "şehir", "agir" hemû bi vê format in: ''bêdeng'' (e, i, a) + ''dengdêr'' (s, ş, h, k, g) + -ir. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:03, 24 tîrmeh 2026 (UTC)
::::::Fistikçi şahap dîsa hat pêşiya me :D difire difre? Firin firnê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:05, 24 tîrmeh 2026 (UTC)
a1ngc5dgue86yu79e036ivloonjtpnn
6277021
6277008
2026-07-24T00:42:15Z
Ghybu
1393
/* neşir, heşir */ Bersiv
6277021
wikitext
text/x-wiki
{{arşîvkirin|1=
* [[Gotûbêja modulê:ku-tewîn-nav/arşîv 1|Arşîv 1]]
}}
== neşir, heşir ==
@[[Bikarhêner:Ghybu|Ghybu]] Gelo ''nesir, neşir, heşir, şehir'' bikin ''nesrê, neşrê, heşrê, şehrê'' li şûna ''nesirê, neşirê, heşirê, şehirê''? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:06, 23 tîrmeh 2026 (UTC)
:Fikir, fikrî ne fikirî @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] fikrê we? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:43, 23 tîrmeh 2026 (UTC)
::Erê, bi kurdî bilêvkirina ''-irê''/''-irî'' zor e... Ji bo problemên wisa (demak ku ne sîstematîk e) em dikarin parametreyeke wekî <code>|tew=fikr</code> çêkin: tewandin li gorî parametreya <code>tew</code> tê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:33, 23 tîrmeh 2026 (UTC)
:::Erê parametre hewce ye min got belkî otomatîk dibe feqat na min îsmên bi -ir xilas dibin nêrî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/ceribandin&oldid=6277002] hemû nabin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:54, 23 tîrmeh 2026 (UTC)
::::Gelo tenê kelîmeyên zimanên din de wisa dibe? Kes "agrê" dibêje li şûna "agirê"? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 23 tîrmeh 2026 (UTC)
:::::Li vira peyvên "fikir", "nesir", "heşir", "şehir", "agir" hemû bi vê format in: ''bêdeng'' (e, i, a) + ''dengdêr'' (s, ş, h, k, g) + -ir. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:03, 24 tîrmeh 2026 (UTC)
::::::Fistikçi şahap dîsa hat pêşiya me :D difire difre? Firin firnê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:05, 24 tîrmeh 2026 (UTC)
:::::::Erê, "firin" jî wekî: ''bêdeng'' (i) + ''dengdêr'' (r) + ''-in'' e... Herwîha hemû du kite ne jî. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:42, 24 tîrmeh 2026 (UTC)
0fwiqs01jcluhtkmsouempnmb8rglk5
Modul:parameters
828
770270
6277069
6240792
2026-07-24T07:44:35Z
Wikihez
7344
6277069
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(
"Parametre%s %s.",
format(n == 1 and "ya %s" or "yên %s", concat_list(list, " û ", true)),
format(n == 1 and "%s nayê bikaranîn" or "%s nayên bikaranîn", msg),
"bikaranîn"
), 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(
"Parametreya %s çend caran hatiye nivîsin. Mihtemelen ji ber ku bernavê(alias) parametreyekê hatiye bikaranîn.",
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, "di vê şablonê de")
end
return args_new
end
return export
bc2xrp63be26khxxfxby3qylqejbkpm
6277070
6277069
2026-07-24T07:46:29Z
Wikihez
7344
6277070
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(
"Parametre%s %s %s.",
format(n == 1 and "ya %s" or "yên %s", concat_list(list, " û ", true)),
format(n == 1 and "%s nayê" or "%s nayên", msg),
"bikaranîn"
), 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(
"Parametreya %s çend caran hatiye nivîsin. Mihtemelen ji ber ku bernavê(alias) parametreyekê hatiye bikaranîn.",
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, "di vê şablonê de")
end
return args_new
end
return export
g2nenb1ybie52s5nfsxiwqy39g4lw0z
Bikarhêner:Ghybu/test4
2
837764
6276952
6276859
2026-07-23T19:03:30Z
Ghybu
1393
6276952
wikitext
text/x-wiki
# {{ceribandin2|heval|mê û nêr|f=hevala}}
# {{ceribandin2|heval|mê û nêr|f=hevalo}}
tfpywcfy5rpb54tr64896q7rp90i8ar
6276964
6276952
2026-07-23T19:33:55Z
Ghybu
1393
6276964
wikitext
text/x-wiki
# {{ceribandin2|heval|mê û nêr|f=hevalekî}}
# {{ceribandin2|heval|mê û nêr|f=hevalo}}
aefrtwwahigiuvey3q4g70yw31saaif
6277062
6276964
2026-07-24T03:14:10Z
Ghybu
1393
Rûpel hat valakirin
6277062
wikitext
text/x-wiki
phoiac9h4m842xq45sp7s6u21eteeq1
Binefş
0
873407
6276933
6196515
2026-07-23T12:40:11Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6276933
wikitext
text/x-wiki
{{bnr-h|binefş}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Serenav ===
{{serenav|ku|z=m}}
# [[nav|Nav]]ek [[jin]]an e.
==== Etîmolojî ====
Ji {{q|ku|binefş}}.
==== Werger ====
{{werger-ser}}
{{werger-bin}}
647ovo1558lnpzv7ogog7hkmxl6m1e9
Kategorî:Madeyên bê beşa mane-biwêj bi kurmancî
14
873632
6276953
5883987
2026-07-23T19:18:19Z
Wikihez
7344
Wikihez rûpela [[Kategorî:Gotarên bê beşa mane-biwêj bi kurmancî]] bêyî beralîkirinek bar kir [[Kategorî:Madeyên bê beşa mane-biwêj bi kurmancî]]
5883987
wikitext
text/x-wiki
[[Kategorî:Gotarên bê beşa mane bi kurmancî|biwêj]]
8ahmwnb5j8wn5i5aa7vw4dz5fe8fh9b
6276954
6276953
2026-07-23T19:18:34Z
Wikihez
7344
6276954
wikitext
text/x-wiki
[[Kategorî:Madeyên bê beşa mane bi kurmancî|biwêj]]
61yhstfayea2gqz0qskeantdddnehfs
Bikarhêner:Wikihez/common.css
2
874440
6276980
6265642
2026-07-23T22:04:36Z
Wikihez
7344
6276980
css
text/css
/* [[Şablon:manelazim]]: dema ku amûra "Pênaseyê temam bike" bi awayekî serkeftî were
barkirin, nivîsa cîgir a "manelazim" tê veşartin; ji bo bikarhênerên bêyî
JavaScript, ev nivîs xuya dibe (ji ber vê yekê em .manelazim bi xwe naşehitînin). */
.fill-definition-resolved > .manelazim {
display: none;
}
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
gar8b9gdtuz2tsba2houfhji4klxsij
6276988
6276980
2026-07-23T22:48:32Z
Wikihez
7344
6276988
css
text/css
/* [[Şablon:manelazim]]: dema ku amûra "Pênaseyê temam bike" bi awayekî serkeftî were
barkirin, nivîsa cîgir a "manelazim" tê veşartin; ji bo bikarhênerên bêyî
JavaScript, ev nivîs xuya dibe (ji ber vê yekê em .manelazim bi xwe naşehitînin). */
.fill-definition-resolved > .manelazim {
display: none;
}
.jeder {
display: revert !important;
}
rsfwioqobd2x96hvn77v2hd9lqeukzd
6276999
6276988
2026-07-23T23:44:20Z
Wikihez
7344
6276999
css
text/css
/* [[Şablon:manelazim]]: dema ku amûra "Pênaseyê temam bike" bi awayekî serkeftî were
barkirin, nivîsa cîgir a "manelazim" tê veşartin; ji bo bikarhênerên bêyî
JavaScript, ev nivîs xuya dibe (ji ber vê yekê em .manelazim bi xwe naşehitînin). */
.fill-definition-resolved > .manelazim {
display: none;
}
.jeder {
display: revert !important;
}
*/
5ayiltj0rdk5s75fgh4ar1ii34qr566
6277000
6276999
2026-07-23T23:44:33Z
Wikihez
7344
6277000
css
text/css
/* [[Şablon:manelazim]]: dema ku amûra "Pênaseyê temam bike" bi awayekî serkeftî were
barkirin, nivîsa cîgir a "manelazim" tê veşartin; ji bo bikarhênerên bêyî
JavaScript, ev nivîs xuya dibe (ji ber vê yekê em .manelazim bi xwe naşehitînin). */
.fill-definition-resolved > .manelazim {
display: none;
}
.jeder {
/* display: revert !important;*/
}
5a7o3w9p84x02bvhesx2b7blaq6my2g
6277009
6277000
2026-07-24T00:29:16Z
Wikihez
7344
Restored revision 6276999 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277009
css
text/css
/* [[Şablon:manelazim]]: dema ku amûra "Pênaseyê temam bike" bi awayekî serkeftî were
barkirin, nivîsa cîgir a "manelazim" tê veşartin; ji bo bikarhênerên bêyî
JavaScript, ev nivîs xuya dibe (ji ber vê yekê em .manelazim bi xwe naşehitînin). */
.fill-definition-resolved > .manelazim {
display: none;
}
.jeder {
display: revert !important;
}
*/
5ayiltj0rdk5s75fgh4ar1ii34qr566
6277012
6277009
2026-07-24T00:31:25Z
Wikihez
7344
Restored revision 6277000 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277012
css
text/css
/* [[Şablon:manelazim]]: dema ku amûra "Pênaseyê temam bike" bi awayekî serkeftî were
barkirin, nivîsa cîgir a "manelazim" tê veşartin; ji bo bikarhênerên bêyî
JavaScript, ev nivîs xuya dibe (ji ber vê yekê em .manelazim bi xwe naşehitînin). */
.fill-definition-resolved > .manelazim {
display: none;
}
.jeder {
/* display: revert !important;*/
}
5a7o3w9p84x02bvhesx2b7blaq6my2g
Şablon:jêder-kovar/ceribandin
10
970861
6276958
5021028
2026-07-23T19:22:04Z
Wikihez
7344
6276958
wikitext
text/x-wiki
{{#invoke:ceribandin/Wikihez|kovar_t}}<noinclude><!--
-->{{documentation}}</noinclude>
lmjihy10hp98ttkvkhjbitcicjod975
6277047
6276958
2026-07-24T02:06:40Z
Wikihez
7344
6277047
wikitext
text/x-wiki
<includeonly>{{#invoke:quote|quote_t|type=journal}}</includeonly><!--
--><noinclude>{{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}<!--
-->{{documentation}}</noinclude>
dkexb051cxt14yab1y7i26tidjh3ljj
Şablon:jêder-malper/ceribandin
10
970862
6276959
5021027
2026-07-23T19:22:12Z
Wikihez
7344
6276959
wikitext
text/x-wiki
{{#invoke:ceribandin/Wikihez|kovar_t}}<noinclude><!--
-->{{documentation}}</noinclude>
lmjihy10hp98ttkvkhjbitcicjod975
6276960
6276959
2026-07-23T19:22:26Z
Wikihez
7344
6276960
wikitext
text/x-wiki
{{#invoke:ceribandin/Wikihez|malper_t}}<noinclude><!--
-->{{documentation}}</noinclude>
4wd1kphinr8o7tmnxvl1bd5he8gon34
6277045
6276960
2026-07-24T02:04:43Z
Wikihez
7344
6277045
wikitext
text/x-wiki
<includeonly>{{#invoke:quote|quote_t
| alias =
chapter: title,webpage,entry;
trans-chapter: trans-title,trans-webpage,trans-entry;
chapterurl: titleurl,webpageurl,entryurl;
chapter_tlr: webpage_tlr;
chapter_series: webpage_series;
chapter_seriesvolume: webpage_seriesvolume;
chapter_number: webpage_number;
chapter_plain: title_plain,webpage_plain;
title: site,work;
trans-title: trans-site,trans-work;
}}</includeonly><!--
--><noinclude>{{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}<!--
-->{{documentation}}</noinclude>
sq66l1pd7ncsap873ht1ieqtkcqdvgi
Bikarhêner:Wikihez/Gadget-linkify.js
2
974481
6276928
6276895
2026-07-23T12:32:06Z
Wikihez
7344
6276928
javascript
text/javascript
// MediaWiki gadget: automatically link selected text to existing wiktionary entries
(function($) {
var messages = {
ku: {
toolbarButtonName: "Li nivîsa bijartî bi otomatîkî wîkîlînk lê zêde bike",
delinkifyButtonName: "Ji nivîsa bijartî wîkîlînkan rake",
caseChangeLabel: "Nivîsa bijartî MEZIN/biçûk bike",
caseUpperLabel: "HEMÛ MEZIN",
caseLowerLabel: "hemû biçûk",
caseSentenceLabel: "Wekî cimleyan",
caseTitleLabel: "Herfa Ewil Mezin"
},
en: {
toolbarButtonName: "Automatically link selected text",
delinkifyButtonName: "Remove links from selected text",
caseChangeLabel: "Change letter case",
caseUpperLabel: "UPPERCASE",
caseLowerLabel: "lowercase",
caseSentenceLabel: "Sentence case",
caseTitleLabel: "Title Case"
},
es: {
toolbarButtonName: "Automáticamente insertar enlaces en la selección",
delinkifyButtonName: "Quitar enlaces del texto seleccionado",
caseChangeLabel: "Cambiar mayúsculas/minúsculas",
caseUpperLabel: "MAYÚSCULAS",
caseLowerLabel: "minúsculas",
caseSentenceLabel: "Case de oración",
caseTitleLabel: "Case de Título"
}
};
// Kurmanji function words that should never become links on their own,
// plus the 8 bare Kurmanji vowels alone.
var ignoredWordList = (
"a|an|ba|bê|bi|çi|çu|çû|da|de|dê|dî|di|e|ê|ên|erê|ev|ew|em|gel|ha|hê|her|" +
"hûn|i|î|jê|jî|ka|kî|ku|kû|lê|li|me|min|na|ne|o|pê|re|ra|ti|te|tê|" +
"tu|u|û|va|van|ve|vê|vî|wan|we|wê|wî|wir|vir|ya|yan|yê|yên"
).split("|");
var ignoredWords = {};
$.each(ignoredWordList, function(i, word) {
ignoredWords[word] = true;
});
/**
* One less than the maximum number of syllables to be considered as a compound
* word.
*/
var lookahead = 2;
// Token boundary: uppercase Latin A-Z, Kurmanji diacritic letters (Ç ê Î Ş Û),
// combining marks, and wiki markup delimiters.
var tokenRE = /(\s*(?:<!--[\s\S]+?-->|\{\{[\s\S]+?\}\}|\[\[.+?\]\]|[^A-ZÇÊÎŞÛçêîşû\u0300-\u036F{[< ]+|[{[<])\s*)/i;
// Matches the "{{ziman|ku}}" language-section template that marks an
// entry as actually having a Kurdish section. Templates are written by
// hand, so be forgiving of stray whitespace around the pipe/braces, but
// don't try to handle template redirects/aliases here.
var kuZimanRE = /\{\{\s*ziman\s*\|\s*ku\s*\}\}/i;
// "=== Formeke navdêrê ===" but also allow a numbered variant, e.g.
// "=== Formeke navdêrê 2 ===", which shows up when a page has more than
// one noun-form section.
var kuFormekeNavdereRE = /^\s*=+\s*Formeke navdêrê(?:\s+\d+)?\s*=+\s*$/im;
String.prototype.linkifyUncapitalize = function() {
return this[0].toLowerCase() + this.substr(1);
};
// Kurmanji noun-declension suffixes, longest first so the greedy match
// prefers the most specific ending. Generous rather than precise: every
// candidate root is verified against real wiktionary entries before use,
// so a bad strip is just discarded, never turned into a wrong link.
var nounSuffixes = [
"iyekî", "iyekê", "iyeke", "iyinan", "iyino",
"yekî", "yekê", "yeke", "yinan", "yino",
"ekî", "ekê", "eke", "inan", "ine", "ino",
"iyên", "iyan", "iyek", "iyin", "iyo", "iyî", "iyê", "iya",
"yên", "yan", "yek", "yin", "yo", "yî", "yê", "ya",
"ek", "ên", "an", "in",
"a", "ê", "î", "o"
];
/**
* Given a lowercase surface word, returns candidate noun roots obtained by
* stripping a known declension suffix -- e.g. "rûpeleke" -> ["rûpel"].
* Longest remaining root first, so safer candidates are tried first.
*/
function getNounStemCandidates(word) {
var candidates = [];
for (var i = 0; i < nounSuffixes.length; i++) {
var suf = nounSuffixes[i];
// Require a 2+ character root, to avoid degenerate single letters.
if (word.length > suf.length + 1 && word.slice(-suf.length) === suf) {
candidates.push(word.slice(0, -suf.length));
}
}
// "giranî" + "yê" -> "giraniyê" drops the circumflex, so stripping "yê"
// gives "girani" not "giranî". Restore the î in place -- don't keep
// "girani" around too, it's invalid Kurdish orthography and would just
// be dead weight in the existence check.
candidates = candidates.map(function(root) {
if (root.length > 1 && root.slice(-1) === "i") {
return root.slice(0, -1) + "î";
}
return root;
});
candidates.sort(function(a, b) {
return b.length - a.length;
});
return candidates;
}
// Words this short are too ambiguous / noisy to bother linking, even if
// they aren't in ignoredWords and even if a real entry exists for them.
var MIN_LINKABLE_LENGTH = 3;
// MediaWiki batches titles up to 50 per request for normal users (500 for
// bots). Used both for the stem-confirmation content check and for the
// {{ziman|ku}} content check below.
var TITLE_BATCH_SIZE = 50;
/**
* Splits an array of titles into chunks of TITLE_BATCH_SIZE, so a single
* selection with a lot of unique words never trips the API's per-request
* titles limit.
*/
function chunkTitles(titles) {
var chunks = [];
for (var i = 0; i < titles.length; i += TITLE_BATCH_SIZE) {
chunks.push(titles.slice(i, i + TITLE_BATCH_SIZE));
}
return chunks;
}
var userLang = mw.config.get("wgUserLanguage");
function getMessage(name) {
return (messages[userLang] || messages.ku)[name] || messages.ku[name];
}
$.kuLinkify = function(text, callback, debug) {
var tokens = text.split(tokenRE);
var displayTokens = [];
var words = {};
var existingLinks = {};
// Surface words (lowercase-first-letter form) that have already been
// assigned a link during this run -- prevents the same word from being
// linked twice, even if it resolves to a different target (stem vs.
// direct entry vs. redlink) on a later occurrence.
var linkedWords = {};
// surface word -> candidate noun roots, e.g. stemCandidates["yekeya"] = ["yeke", "yekey"]
// Pruned by processStemCheckResults() below: kept if the surface word has
// no page of its own (nothing to contradict the stem), or if its page is
// confirmed as an inflected form (has "Formeke navdêrê"). Deleted if its
// page exists but ISN'T marked as a form -- that's a real headword, not
// something to stem.
var stemCandidates = {};
$.each(tokens, function(i, token) {
if (debug) displayTokens.push(mw.html.escape(token));
var tokenLinks = token.match(/\[\[.+?(?:\|.+?)?\]\]/g);
$.each(tokenLinks || [], function() {
var parts = this.match(/\[\[(.+?)(?:\|(.+?))?\]\]/);
var target = parts[1];
var display = parts[2] !== undefined ? parts[2] : target;
existingLinks[target] = true;
if (display) linkedWords[display.linkifyUncapitalize()] = true;
});
if (!token || token.match(tokenRE)) return;
var tokenWords = token.split(" ");
$.each(tokenWords, function(j, word) {
for (var k = 0; k < lookahead + 1 && tokenWords[j + k]; k++) {
if (k) word += " " + tokenWords[j + k];
if (!ignoredWords[word]) words[word] = true;
if (word[0] != word[0].toLowerCase()) {
var lowerWord = word.linkifyUncapitalize();
if (!ignoredWords[lowerWord]) {
words[lowerWord] = true;
}
}
}
});
// Collect noun-root candidates per single word (declension endings
// don't span multi-word compounds). Provisional until checked
// against page content below.
$.each(tokenWords, function(j, word) {
if (!word) return;
var lower = word.linkifyUncapitalize();
var stems = getNounStemCandidates(lower);
if (stems.length) {
stemCandidates[word] = stems;
}
});
if (debug) {
displayTokens[i] = "<b style='border: 1px solid gray;'>" + mw.html.escape(token) + "</b>";
}
});
if (debug) {
var list = "<pre id='linkify-tokens'>\n" + displayTokens.join("") + "\n</pre>";
$("#linkify-progress").html(list).dialog({
modal: true
});
}
mw.loader.using("mediawiki.api", function() {
var api = new mw.Api();
// Words that MATCHED a declension-suffix pattern need a content
// check before we trust the stem:
// - own page exists AND has "Formeke navdêrê" -> confirmed form, use stem
// - own page exists but NO "Formeke navdêrê" -> real headword, don't stem
// - own page doesn't exist at all -> nothing to check against,
// keep the stem candidate
// and let it be validated
// normally (does the STEM
// exist?) in the main pass
var stemCheckWords = [];
$.each(stemCandidates, function(word) {
stemCheckWords.push(word);
});
function processStemCheckResults(formPageExists, confirmedForms, then) {
$.each(stemCandidates, function(word) {
if (formPageExists[word] && !confirmedForms[word]) {
delete stemCandidates[word];
}
});
$.each(stemCandidates, function(word, stems) {
$.each(stems, function(s, stem) {
if (!ignoredWords[stem]) words[stem] = true;
});
});
then();
}
/**
* Given the set of page titles that exist (from prop=links
* "exists"), fetches their content and keeps only the ones that
* actually contain a {{ziman|ku}} section. A page "existing" only
* means SOME wiki has an entry there -- e.g. "away" exists as an
* English entry, so without this check "awayî" would get wrongly
* linked to [[away]] instead of stemmed to [[awa]]. Batched the
* same way as the stem-confirmation query above.
*/
function filterToKuEntries(existingTitles, then) {
var titles = [];
$.each(existingTitles, function(title) {
titles.push(title);
});
if (!titles.length) {
then({});
return;
}
var kuLinks = {};
var chunks = chunkTitles(titles);
var pending = chunks.length;
function handleChunk(chunk) {
api.get({
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
titles: chunk.join("|"),
format: "json"
}).done(function(result) {
var normMap = {};
if (result.query && result.query.normalized) {
$.each(result.query.normalized, function(i, n) {
normMap[n.to] = n.from;
});
}
var pages = (result.query && result.query.pages) || {};
$.each(pages, function(pageId, page) {
if (!page.revisions || !page.revisions.length) return;
var originalTitle = normMap[page.title] || page.title;
var rev = page.revisions[0];
var content = (rev.slots && rev.slots.main && rev.slots.main["*"]) || rev["*"] || "";
if (kuZimanRE.test(content)) {
kuLinks[originalTitle] = true;
}
});
}).fail(function(error) {
// Content check failed for this chunk -- be
// conservative and don't link any of its titles,
// rather than risk linking to a non-Kurdish entry.
console.warn(error);
}).always(function() {
pending--;
if (pending === 0) then(kuLinks);
});
}
$.each(chunks, function(i, chunk) {
handleChunk(chunk);
});
}
function runLinkExistenceCheck() {
var uniqueWords = [];
$.each(words, function(word) {
if (!ignoredWords[word]) uniqueWords.push(word);
});
if (debug) {
var uniqueLinks = [];
$.each(existingLinks, function(link) {
uniqueLinks.push(link);
});
var stemDebug = [];
$.each(stemCandidates, function(word, stems) {
stemDebug.push(word + " -> " + stems.join(", "));
});
var list = "<hr /><p>Words to search for:</p><p>" + uniqueWords + "</p>";
list += "<hr /><p>Words that already have links:</p><p>" + uniqueLinks + "</p>";
list += "<hr /><p>Stem candidates kept:</p><p>" + stemDebug.join("<br>") + "</p>";
$("#linkify-progress").append(list);
}
if (!uniqueWords.length) return;
api.get({
action: "parse",
text: "[[" + uniqueWords.join("]] [[") + "]]",
prop: "links"
}).done(function(results) {
$("#linkify-progress").append("<hr />");
var displayLinks = [];
var existsAnywhere = {};
$.each(results.parse.links, function() {
if (debug && "exists" in this) displayLinks.push(this["*"]);
if ("exists" in this) existsAnywhere[this["*"]] = true;
});
if (debug) {
$("#linkify-progress").append("<p>Existing entries (any language):</p><p>" + displayLinks + "</p>");
}
// A page existing isn't enough -- confirm it actually has
// a {{ziman|ku}} section before we're willing to link to
// it. `links` from here on means "exists AND is Kurdish".
filterToKuEntries(existsAnywhere, function(links) {
if (debug) {
var kuDisplayLinks = [];
$.each(links, function(title) {
kuDisplayLinks.push(title);
});
$("#linkify-progress").append("<p>Confirmed {{ziman|ku}} entries:</p><p>" + kuDisplayLinks + "</p>");
}
$.each(tokens, function(i, token) {
if (!token || token.match(tokenRE)) return;
var tokenWords = token.split(" ");
for (var j = 0; j < tokenWords.length; j++) {
if (!tokenWords[j]) continue;
var word = [];
for (var k = 0; k < lookahead + 1; k++) {
word.push(tokenWords[j + k]);
}
for (var k = lookahead; k >= 0; k--) {
var compoundWord = word.join(" ");
if (k === 0) {
var lowerSingle = compoundWord.linkifyUncapitalize();
// Never link words this short, even if a real
// entry exists for them.
if (compoundWord.length < MIN_LINKABLE_LENGTH) break;
// Only link the first occurrence of a given
// surface word; skip repeats.
if (linkedWords[lowerSingle]) break;
var stems = stemCandidates[compoundWord] || stemCandidates[lowerSingle] || [];
// 1. Confirmed form, or no own page -- try the stem.
var stemLinked = false;
for (var s = 0; s < stems.length; s++) {
var stem = stems[s];
if ((stem in links) && !(stem in existingLinks) && !ignoredWords[stem]) {
existingLinks[stem] = true;
linkedWords[lowerSingle] = true;
tokenWords[j] = "[[" + stem + "|" + tokenWords[j] + "]]";
stemLinked = true;
break;
}
}
if (stemLinked) break;
if (!ignoredWords[compoundWord] && !ignoredWords[lowerSingle]) {
// 2. Exact word (or lowercase form) is itself a real entry.
var directTarget = null;
if (compoundWord in links) {
directTarget = compoundWord;
} else if (lowerSingle in links) {
directTarget = lowerSingle;
}
if (directTarget && !(directTarget in existingLinks)) {
existingLinks[directTarget] = true;
linkedWords[lowerSingle] = true;
tokenWords[j] = "[[" + directTarget + "|" + tokenWords[j] + "]]";
} else if (!directTarget && !(compoundWord in existsAnywhere) && !(compoundWord in existingLinks)) {
// 3. Doesn't exist anywhere yet -- self-link as a
// redlink so it shows up as a page to create,
// e.g. [[peyva|peyva]]. If it exists but only in
// a non-Kurdish language, don't self-link either;
// that would create a redlink over a page that's
// actually occupied by another language's entry.
existingLinks[compoundWord] = true;
linkedWords[lowerSingle] = true;
tokenWords[j] = "[[" + compoundWord + "|" + tokenWords[j] + "]]";
}
}
break;
}
if (compoundWord in links) {
if ((compoundWord in existingLinks) || ignoredWords[compoundWord]) {
j += k;
break;
}
existingLinks[compoundWord] = true;
linkedWords[compoundWord.linkifyUncapitalize()] = true;
tokenWords[j] = "[[" + tokenWords[j];
tokenWords[j + k] += "]]";
j += k;
break;
}
var lowerWord = compoundWord.linkifyUncapitalize();
if (lowerWord in links) {
if ((lowerWord in existingLinks) || ignoredWords[lowerWord]) {
j += k;
break;
}
existingLinks[lowerWord] = true;
linkedWords[lowerWord] = true;
tokenWords[j] = "[[" + lowerWord + "|" + tokenWords[j];
tokenWords[j + k] += "]]";
j += k;
break;
}
word.splice(-1, 1);
}
}
tokens[i] = tokenWords.join(" ");
if (debug) {
displayTokens[i] = "<b style='border: 1px solid gray;'>" + mw.html.escape(tokens[i]) + "</b>";
}
});
if (debug) {
$("#linkify-tokens").html("\n" + displayTokens.join("") + "\n");
}
callback(tokens.join(""));
}); // end filterToKuEntries
}).fail(function(error) {
console.warn(error);
callback();
});
}
if (!stemCheckWords.length) {
processStemCheckResults({}, {}, runLinkExistenceCheck);
return;
}
// NOTE: MediaWiki batches titles up to 50 per request for normal
// users (500 for bots). Chunk stemCheckWords here if it ever grows
// past that for a single selection.
api.get({
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
titles: stemCheckWords.join("|"),
format: "json"
}).done(function(result) {
var confirmedForms = {};
var formPageExists = {};
var normMap = {};
if (result.query && result.query.normalized) {
$.each(result.query.normalized, function(i, n) {
normMap[n.to] = n.from;
});
}
var pages = (result.query && result.query.pages) || {};
$.each(pages, function(pageId, page) {
var originalWord = normMap[page.title] || page.title;
if (!page.revisions || !page.revisions.length) {
formPageExists[originalWord] = false;
return;
}
formPageExists[originalWord] = true;
var rev = page.revisions[0];
var content = (rev.slots && rev.slots.main && rev.slots.main["*"]) || rev["*"] || "";
if (kuFormekeNavdereRE.test(content)) {
confirmedForms[originalWord] = true;
}
});
processStemCheckResults(formPageExists, confirmedForms, runLinkExistenceCheck);
}).fail(function() {
// Content check failed -- be conservative: treat every candidate
// as if its page exists but isn't confirmed, so stemming is
// skipped rather than risking a wrong link.
var formPageExists = {};
$.each(stemCandidates, function(word) {
formPageExists[word] = true;
});
processStemCheckResults(formPageExists, {}, runLinkExistenceCheck);
});
});
}
// Matches a single wikilink: [[target]] or [[target|display]] (also
// File:/Category: links, unwrapped the same way).
var wikilinkRE = /\[\[([^\]|]*)\|([^\]]*)\]\]|\[\[([^\]|]*)\]\]/g;
/**
* Strips wikilink markup, keeping only the reader-facing text.
* [[target|display]] -> display, [[target]] -> target.
*/
$.kuDelinkify = function(text) {
return text.replace(wikilinkRE, function(match, pipedTarget, pipedDisplay, bareTarget) {
return pipedDisplay !== undefined ? pipedDisplay : bareTarget;
});
};
// ---------------------------------------------------------------
// Letter-case conversion (UPPERCASE / lowercase / Sentence case /
// Title Case). Kurmanji-only, so plain JS toUpperCase()/toLowerCase()
// already do the right thing. Title Case reuses ignoredWords as the
// stopword list for small grammatical words.
// ---------------------------------------------------------------
var KU_LETTER_CLASS = "a-zA-ZçêîşûÇÊÎŞÛ";
var kuNonLetterLeadingRE = new RegExp("^[^" + KU_LETTER_CLASS + "]*");
var kuNonLetterTrailingRE = new RegExp("[^" + KU_LETTER_CLASS + "]*$");
var kuSentenceBreakRE = new RegExp(
"(^\\s*[a-zçêîşû]|[.!?][\"'\\)\\]]*\\s+[a-zçêîşû])", "gi"
);
function kuCapitalizeFirstLetter(word) {
if (word === "") return word;
var lowered = word.toLowerCase();
return lowered.charAt(0).toUpperCase() + lowered.slice(1);
}
function kuToSentenceCase(text) {
var lowered = text.toLowerCase();
return lowered.replace(kuSentenceBreakRE, function(match) {
var idx = match.length - 1;
return match.slice(0, idx) + match.charAt(idx).toUpperCase();
});
}
// Capitalizes one word chunk: handles leading/trailing punctuation,
// hyphenated compounds, and keeps ignoredWords lowercase unless forced
// (first word, or right after an opening bracket/quote).
function kuCapitalizeWordChunk(chunk, forceCapitalize) {
var leading = (chunk.match(kuNonLetterLeadingRE) || [""])[0];
var trailing = (chunk.match(kuNonLetterTrailingRE) || [""])[0];
var core = chunk.slice(leading.length, chunk.length - trailing.length);
if (core === "") return chunk;
if (/[(["'‘“]/.test(leading)) forceCapitalize = true;
var loweredCore = core.toLowerCase();
var isStopWord = !forceCapitalize && ignoredWords[loweredCore];
var newCore;
if (isStopWord) {
newCore = loweredCore;
} else {
newCore = core.split("-").map(function(part) {
return kuCapitalizeFirstLetter(part);
}).join("-");
}
return leading + newCore + trailing;
}
function kuToTitleCase(text) {
var wordIndex = 0;
return text.split(/(\s+)/).map(function(chunk) {
if (chunk === "" || /^\s+$/.test(chunk)) return chunk;
var isFirstWord = wordIndex === 0;
wordIndex++;
return kuCapitalizeWordChunk(chunk, isFirstWord);
}).join("");
}
$.kuApplyCase = function(text, form) {
switch (form) {
case "upper":
return text.toUpperCase();
case "lower":
return text.toLowerCase();
case "sentence":
return kuToSentenceCase(text);
case "title":
return kuToTitleCase(text);
default:
return text;
}
};
// ---------------------------------------------------------------
// Dark-mode-aware toolbar icons. Vector 2022's day/night/os switch
// marks its active theme with a class on <html>
// (skin-theme-clientpref-{day,night,os}); when that class says "os",
// or isn't present at all (older skins, no theme switcher), fall back
// to the browser's own prefers-color-scheme signal.
// ---------------------------------------------------------------
var kuIcons = {
wikiText: {
normal: "//upload.wikimedia.org/wikipedia/commons/a/a0/OOjs_UI_icon_wikiText.svg",
invert: "//upload.wikimedia.org/wikipedia/commons/f/fb/OOjs_UI_icon_wikiText-invert.svg"
},
noWikiText: {
normal: "//upload.wikimedia.org/wikipedia/commons/8/8c/OOjs_UI_icon_noWikiText-rtl.svg",
invert: "//upload.wikimedia.org/wikipedia/commons/4/44/OOjs_UI_icon_noWikiText-rtl-invert.svg"
},
searchCaseSensitive: {
normal: "//upload.wikimedia.org/wikipedia/commons/e/e5/OOjs_UI_icon_searchCaseSensitive.svg",
invert: "//upload.wikimedia.org/wikipedia/commons/0/02/OOjs_UI_icon_searchCaseSensitive-invert.svg"
}
};
function kuIsDarkMode() {
var $html = $(document.documentElement);
if ($html.hasClass("skin-theme-clientpref-night")) return true;
if ($html.hasClass("skin-theme-clientpref-day")) return false;
return !!(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches);
}
function kuGetIcon(name) {
var pair = kuIcons[name];
return kuIsDarkMode() ? pair.invert : pair.normal;
}
function getLinkifyTool() {
return {
type: "button",
action: {
type: "callback",
execute: function() {
mw.loader.using("jquery.textSelection", function() {
var selection = $("#wpTextbox1").textSelection("getSelection");
$.kuLinkify(selection, function(linkedText) {
if (!linkedText) return;
$("#wpTextbox1").textSelection("encapsulateSelection", {
peri: linkedText,
replace: true
});
}, $.kuLinkify_debug);
});
}
},
label: getMessage("toolbarButtonName"),
icon: kuGetIcon("wikiText")
};
}
function getDelinkifyTool() {
return {
type: "button",
action: {
type: "callback",
execute: function() {
mw.loader.using("jquery.textSelection", function() {
var selection = $("#wpTextbox1").textSelection("getSelection");
var plainText = $.kuDelinkify(selection);
if (plainText === selection) return;
$("#wpTextbox1").textSelection("encapsulateSelection", {
peri: plainText,
replace: true
});
});
}
},
label: getMessage("delinkifyButtonName"),
icon: kuGetIcon("noWikiText")
};
}
// Applies a case transform to the current textbox selection in place.
function applyCaseToSelection(form) {
mw.loader.using("jquery.textSelection", function() {
var selection = $("#wpTextbox1").textSelection("getSelection");
if (!selection) return;
var converted = $.kuApplyCase(selection, form);
if (converted === selection) return;
$("#wpTextbox1").textSelection("encapsulateSelection", {
peri: converted,
replace: true
});
});
}
// Hand-built dropdown (wikiEditor "element" tool) instead of the built-in
// "select" type, since that one's close-on-select/re-click behavior isn't
// reliably patchable from a gadget. Reuses wikiEditor's own CSS classes so
// it looks like a native dropdown.
function buildCaseChangeElement() {
var forms = [{
key: "upper",
label: getMessage("caseUpperLabel")
},
{
key: "lower",
label: getMessage("caseLowerLabel")
},
{
key: "sentence",
label: getMessage("caseSentenceLabel")
},
{
key: "title",
label: getMessage("caseTitleLabel")
}
];
var $select = $("<div>").addClass("tool tool-select").attr("rel", "caseChange");
var $options = $("<div>").addClass("options");
var $menu = $("<div>").addClass("menu").append($options).hide();
function closeMenu() {
$select.removeClass("options-shown");
$menu.hide();
$label.attr("aria-expanded", "false");
}
function openMenu() {
// Only one such dropdown open at a time.
$(".tool-select").not($select).removeClass("options-shown").find(".menu").hide();
$select.addClass("options-shown");
$menu.show();
$label.attr("aria-expanded", "true");
}
$.each(forms, function(i, form) {
$("<a>")
.addClass("option")
.attr({
rel: form.key,
tabindex: 0,
role: "menuitem"
})
.text(form.label)
.on("mousedown", function(e) {
e.preventDefault();
return false;
})
.on("click keydown", function(e) {
if (e.type === "click" || (e.type === "keydown" && e.key === "Enter")) {
applyCaseToSelection(form.key);
closeMenu();
e.preventDefault();
return false;
}
})
.appendTo($options);
});
var $labelIcon = $("<img>")
.addClass("oo-ui-image")
.attr({
src: kuGetIcon("searchCaseSensitive"),
alt: ""
});
var $label = $("<a>")
.addClass("label")
.attr({
role: "button",
tabindex: 0,
"aria-expanded": "false",
"aria-haspopup": "menu",
title: getMessage("caseChangeLabel")
})
.css({
display: "inline-flex",
"align-items": "center",
gap: "4px"
})
.append($labelIcon, $("<span>").text("Mezin/biçûk"))
.on("mousedown", function(e) {
e.preventDefault();
return false;
})
.on("click keydown", function(e) {
if (e.type === "click" || (e.type === "keydown" && e.key === "Enter")) {
if ($select.hasClass("options-shown")) {
closeMenu();
} else {
openMenu();
}
e.preventDefault();
return false;
}
});
$select.append($label, $menu);
// Close on any click elsewhere. Re-bound via .off() first so
// re-registering this tool never stacks up duplicate handlers.
$(document).off("mousedown.kuCaseChange").on("mousedown.kuCaseChange", function(e) {
if (!$(e.target).closest($select).length) {
closeMenu();
}
});
return $select;
}
function getCaseChangeTool() {
return {
type: "element",
element: buildCaseChangeElement
};
}
function addLinkifyButton() {
var $textbox = $("#wpTextbox1");
// No edit form on this page (VisualEditor, view/history page, etc.).
if (!$textbox.length) return;
if ($.kuLinkify) {
$textbox.after($(mw.html.element("div", {
id: "linkify-progress",
title: getMessage("toolbarButtonName")
})).hide());
}
// Single source of truth so both registration paths below never
// build duplicate tool objects.
function buildTools() {
return {
linkify: getLinkifyTool(),
delinkify: getDelinkifyTool(),
caseChange: getCaseChangeTool()
};
}
// Guards against adding the tools twice: the buildSection event and
// the mw.loader.using callback below can both fire on the same load.
var toolsAdded = false;
// Rebuilds the kutools toolbar group so its icons pick up whichever
// light/dark icon variant matches the theme active right now --
// used when the day/night/os toggle or the OS-level color scheme
// changes without a page reload.
function rebuildIcons() {
var context = $textbox.data("wikiEditor-context");
if (!context || !context.modules || !context.modules.toolbar) return;
$textbox.wikiEditor("removeFromToolbar", {
section: "main",
group: "kutools"
});
$textbox.wikiEditor("addToToolbar", {
section: "main",
groups: {
kutools: {
tools: buildTools()
}
}
});
}
// Bind synchronously (not inside mw.loader.using's callback) so we
// don't miss the event if ext.wikiEditor resolves instantly/cached.
$textbox.on('wikiEditor-toolbar-buildSection-main', function(evt, section) {
section.groups.kutools = {
tools: buildTools()
};
toolsAdded = true;
});
mw.loader.using("ext.wikiEditor", function () {
if (toolsAdded) return;
// Fallback: toolbar was already built (and the event already fired)
// before this script ran -- add tools directly via the public API.
var context = $textbox.data("wikiEditor-context");
if (context && context.modules && context.modules.toolbar) {
$textbox.wikiEditor("addToToolbar", {
section: "main",
groups: {
kutools: {
tools: buildTools()
}
}
});
toolsAdded = true;
}
});
// Watch for the OS-level color scheme changing (system dark mode
// toggled while the page is open).
if (window.matchMedia) {
var kuDarkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
var kuOnSchemeChange = function() {
if (!toolsAdded) return;
rebuildIcons();
};
if (kuDarkModeQuery.addEventListener) {
kuDarkModeQuery.addEventListener("change", kuOnSchemeChange);
} else if (kuDarkModeQuery.addListener) {
// Older browsers.
kuDarkModeQuery.addListener(kuOnSchemeChange);
}
}
// Watch for Vector 2022's day/night/os toggle, which flips a class
// on <html> without reloading the page.
if (window.MutationObserver) {
var kuThemeObserver = new MutationObserver(function() {
if (!toolsAdded) return;
rebuildIcons();
});
kuThemeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"]
});
}
}
// Only run on edit pages -- avoids a wasted mw.loader.using round trip
// elsewhere, where #wpTextbox1 never exists anyway.
if ($.inArray(mw.config.get("wgAction"), ["edit", "submit"]) !== -1) {
addLinkifyButton();
}
})(jQuery);
4agpw1zzfgrve72tq9pr2hjaw9dctgm
Bikarhêner:Wikihez/Gadget-start.js
2
974484
6276931
6276784
2026-07-23T12:38:10Z
Wikihez
7344
6276931
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage)
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var action = mw.config.get("wgAction");
var pageExists = !!mw.config.get("wgArticleId");
// "Zimanek lê zêde bike" mode: triggered from the portlet link below.
var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1";
// On a normal page view of an existing page, add the portlet link
if (action === "view" && pageExists) {
var targetContainer;
var skin = mw.config.get('skin');
if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Ji vê rûpelê re ziman an jî beşeke nû zêde bike");
return;
}
if (!(action === "edit" || action === "submit")) {
return;
}
if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) {
return;
}
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
// Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra
// whitespace. codePattern: escaped code for one language, or "[^}]+" for any.
function langHeaderSource(codePattern) {
return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Finds a language's header in wikitext (see langHeaderSource). Returns
// { index, match } or null.
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
// Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the
// "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched
// once on first use and cached — every citation row's picker shares it.
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10, // navê-navî Şablon:, her tim 10 ye
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" (an hevwateya wê ya herêmî) rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
// Draft cache for this page — lets the editor manually save/restore the
// whole wizard state (shared lang/pron + every section) via
// [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of
// any single browser session. Keyed per page so different articles'
// drafts don't collide.
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
// Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check.
var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches;
var chosenOptions = {
width: "20em",
disable_search_threshold: 0,
placeholder_text_single: "Yekî hilbijêre",
placeholder_text_multiple: "Hilbijêre",
no_results_text: "Nehat dîtin",
enable_split_word_search: true,
search_contains: true };
function wrapNativeSelect(select) {
var placeholder = select.attr("data-placeholder") || "Lê bigere…";
// Hide the native select — we drive it programmatically.
select.hide();
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide();
// Full option cache — rebuilt when chosen:updated fires.
var allOptions = [];
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
if (!v) return; // skip the empty placeholder option
allOptions.push({
value: v,
text: $(this).text(),
disabled: $(this).prop("disabled")
});
});
}
cacheOptions();
function showResults(query) {
var q = query.trim().toLowerCase();
resultsList.empty();
var matched;
if (q) {
matched = [];
$.each(allOptions, function (_, o) {
if (o.disabled) return;
var codeMatch = o.value.toLowerCase().indexOf(q) !== -1;
var nameMatch = o.text.toLowerCase().indexOf(q) !== -1;
if (codeMatch || nameMatch) {
matched.push({ opt: o, codeMatch: codeMatch });
}
});
// Code matches (especially prefix matches) come first.
matched.sort(function (a, b) {
var aCode = a.opt.value.toLowerCase();
var bCode = b.opt.value.toLowerCase();
var aPrefix = aCode.indexOf(q) === 0;
var bPrefix = bCode.indexOf(q) === 0;
if (aPrefix !== bPrefix) return aPrefix ? -1 : 1;
if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1;
return 0;
});
matched = matched.map(function (item) { return item.opt; });
} else {
matched = allOptions.filter(function (o) { return !o.disabled; });
}
if (!matched.length) {
resultsList.hide();
return;
}
$.each(matched.slice(0, 50), function (_, opt) {
var li = $("<li></li>").text(opt.text).attr("data-value", opt.value);
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// Keep mousedown for desktop fallback.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
resultsList.append(li);
});
// Position the list above or below depending on available viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px ≈ 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
resultsList.show();
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
resultsList.hide();
select.trigger("change");
}
textInput.on("input focus", function () {
showResults($(this).val());
});
textInput.on("blur", function () {
// Short delay so a touchstart on a result item fires first.
setTimeout(function () { resultsList.hide(); }, 200);
});
// chosen:updated → re-cache (called after ziman.json loads).
select.on("chosen:updated", function () {
cacheOptions();
// If a value is already selected, keep the display text in sync.
var cur = select.val();
if (cur) {
var match = $.grep(allOptions, function (o) { return o.value === cur; })[0];
if (match) textInput.val(match.text);
}
});
// No-op .chosen() so desktop call-sites don't need guarding.
select.chosen = function () { return select; };
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
var langsByCode = {};
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
// Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin —
// ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin.
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c[0]] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a
// template-based sense has no "own" gloss for alternate forms, synonyms,
// antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket,
// Nimûne and Jêgirtin stay visible either way.
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{bênimûne|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = text.replace(
new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"),
"'''$1'''");
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + n.tr;
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + n.t;
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 200 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) out += "|tercime=" + jeder.quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
function makeWordListStep(id, name, question_fmt, header, templateName) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
if (words.length === 1) {
// Single word — keep the simple inline template.
return section +
"* {{" + templateName + "|" + lang + "|" +
words[0] + "}}\n";
}
// Multiple words — use {{kol3}} with one positional param per word.
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
} },
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: [],
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
}
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line.charAt(0) === "*" ? line : "* " + line;
}).join("\n");
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var cinsiyet = values.cinsiyet || ""; // from the cinsiyet step
var lekTur = values.lektur || ""; // from the lêker transitivity step
var capitalizedPos = capitalize(pos);
var outputString = "\n=== " + capitalizedPos + " ===\n";
// Add ku-tewîn-nav for single-word navdêr/serenav with known gender.
if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] &&
(pos === "navdêr" || pos === "serenav") && values.lang === "ku") {
outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet];
if (pos === "serenav") outputString += "|y";
outputString += "}}\n";
}
outputString += "{{" + pos + "|" + values.lang;
if (pos === "navdêr" || pos === "serenav") {
if (cinsiyet) {
outputString += "|c=" + cinsiyet;
} else if (values.lang === "ku") {
// Unknown gender — prompt editor to fill it in.
outputString += "|c=?";
}
} else if (/^lêker/.test(pos) && lekTur) {
outputString += "|c=" + lekTur;
}
if (values.tr) {
outputString += "|tr=" + values.tr;
}
outputString += "}}\n";
return outputString;
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function (values) {
var meta = metadata[values.lang];
return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") &&
meta && meta.g && meta.g.length);
},
build: function (stepDiv, values) {
var select = stepDiv.find("select.wiktku-start-step-field");
if (!select.length) {
select = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-cinsiyet-field" }));
stepDiv.append(select);
}
if (!values) return select;
var meta = metadata[values.lang];
var codes = (meta && meta.g) || [];
var prevVal = select.val();
var choices = $.map(codes, function (code) {
return [[code, genderLabel(code)]];
});
populateMenus(select, choices);
if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) {
select.val(prevVal);
}
if (!isMobile) select.trigger("chosen:updated");
return select;
},
output: function () { return ""; } // consumed by pos.output
},
// Lêker transitivity — shown for any lêker variant.
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function (values) {
return values.lang && values.pos && /^lêker/.test(values.pos);
},
// Dictionary: value → display label. Add new types here.
choices: [
["gh", "gerguhêz"],
['x-gh', 'xwerû-gerguhêz'],
['p-gh', 'pêkhatî-gerguhêz'],
['h-gh', 'hevedudanî-gerguhêz'],
['b-gh', 'biwêjî-gerguhêz'],
["ngh", "negerguhêz"],
['x-ngh', 'xwerû-negerguhêz'],
['p-ngh', 'pêkhatî-negerguhêz'],
['h-ngh', 'hevedudanî-negerguhêz'],
['b-ngh', 'biwêjî-negerguhêz'] ],
output: function () { return ""; } // consumed by pos.output
},
tr: {
id: "tr",
name: "transkrîpsiyon",
question_fmt: "Transkrîpsiyona kelîmeyê",
multiple: false,
narrow: true,
visible: function (values) {
return values.lang && values.pos && needsTransliteration(values.lang);
} },
dfn: {
id: "dfn",
// required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne
name: "Mane",
question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!",
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function (stepDiv) {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
dfnArea.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || [];
// Cache of all non-placeholder option elements — used by updateTplOptions
// to remove/re-add options (the only approach compatible with Chosen.js).
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (_, tpl) {
var opt = $("<option></option>")
.val(tpl.name)
.text(tpl.name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible ones.
// This is the only approach that works correctly with both native <select>
// and Chosen.js on desktop.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
if (!isMobile) tplSelect.trigger("chosen:updated");
if (currentVal && !currentStillValid) {
tplSelect.val("");
dfnArea.show();
tplTitleInput.hide().val("");
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpelê…")
.hide();
// Grow the sense list when the title field of the last block gets content,
// mirroring what onDfnInput does for the plain mane textarea.
tplTitleInput.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after programmatically setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
tplTitleInput.show().focus();
} else {
dfnArea.show();
tplTitleInput.hide().val("");
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput);
block.append(tplRow);
block.append(dfnArea);
// Apply lang filter on initial build.
updateTplOptions();
// — Sub-field groups —
// All groups are always visible with a placeholder textarea.
// Nimûne is last in the DOM and last in the output.
var allGroups = [
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in" },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" },
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": ");
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": " + g.izah);
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", g.label + "…");
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow);
});
list.append($("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder (citation) group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text("Jêgirtin: ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
var dragHandle = $("<span></span>")
.addClass("wiktku-jeder-drag-handle")
.attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(dragHandle).append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<input type='text'>")
.addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow")
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi Citoid bîne",
classes: [ "wiktku-dfn-quote-citoid-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoidHelper.ensure().then(function () {
return wikt.citoid.fetchJederFields(query);
}).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
srcSel.trigger("chosen:updated");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
if (!chosen) return;
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
// so the saved value is always exactly what's in that
// (editable) textarea — no separate hidden field that can
// end up empty and silently fall back to "çavkanî?".
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Navê şablonê lê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
function showCatResults(query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
}
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
function selectCatTpl(name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
}
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (_, f) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(f.label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", f.key)
.attr("placeholder", f.label + "…");
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
// Make citation rows sortable (drag to reorder).
// The last row is always the empty trailing row — cancel its drag.
mw.loader.using("jquery.ui.sortable", function () {
jederList.sortable({
handle: ".wiktku-jeder-drag-handle",
axis: "y",
items: "li:not(:last-child)",
tolerance: "pointer"
});
});
jederGroupDiv.append(jederLbl).append(jederList);
block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
return block;
},
/** Grows the sense list when the last mane textarea gets content. */
onDfnInput: function (stepDiv, block, textarea) {
var val = $.trim($(textarea).val());
var list = stepDiv.find("ol.wiktku-dfn-list");
var isLast = block.is(list.children("li:last-child"));
if (val && isLast) {
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
// Apply Chosen to the new block's template selector on desktop.
if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions);
}
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow);
});
list.append($("<li></li>").append(next));
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: [],
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
// True only when at least one sense has a regular mane (needs linkify).
var hasRegularMane = senses.some(function (s) {
return !s.tpl && s.mane !== "";
});
// nimûne/jêder must never pass through linkify, only the mane text.
// defLines (linkified) and citeLines (kept aside) stay parallel;
// a {{wiktku-cite-N}} token in defLines marks where to splice back.
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
$.each(senses, function (_, sense) {
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
var idx = defLines.length;
var cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
if (dfn) {
dfn = stripWikilinks(dfn);
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
} else {
dfn = "{{mane?|" + lang + "}}";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl : "") + (labelTpl ? labelTpl : "") + dfn;
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
var cite = buildCite(sense, lang);
var idx = defLines.length;
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with bênimûne.
text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n";
callback(text);
return;
}
if (!hasRegularMane) {
// All filled senses are template senses — no linkify needed.
callback(restoreCites(text));
return;
}
var didTryToLoad = function () {
if (typeof $.wiktviLinkify === "function") {
$.wiktviLinkify(text, function (linked) {
callback(restoreCites(linked));
});
} else {
callback(restoreCites(text));
}
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"),
binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: [],
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls array.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder,
// Only when a regular sense has content (real mane, or {{mane?}}
// with other fields filled in) — a şablon sense never counts.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (!hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: {
"navz": true},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {
"navz": true
},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
if (!wordLines) return undefined;
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it — nothing to output, and gloss.text is
// empty so there's no caption to give {{werger-ser}} anyway.
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din ên (ne jêgirtinan) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji jêrê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
if (!isMobile) sel.trigger("chosen:updated");
ta.focus();
});
wrap.append(sel).append(status).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
var ordered_steps = [
steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr,
steps.dfn, steps.je, steps.tekildar,
steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani];
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "cinsiyet", "lektur", "tr",
"dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing list so build() recreates it cleanly.
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set programmatically (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true);
});
subList.append($("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text);
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote);
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Notify chosen widgets of the programmatic value change.
if (!isMobile) fields.filter("select").trigger("chosen:updated");
});
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang/pron header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var entry = steps.lang.output(sharedValues);
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) { return s.mane; }).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>"));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice[1]);
if (choice[2] !== undefined) {
// This is a language choice [code, name, sortKey] — show code.
displayText = capitalize(choice[1]) + " (" + choice[0] + ")";
}
var option = $(mw.html.element("option", {
value: choice[0] }, displayText));
if (disabledValues[choice[0]]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
if (isMobile) {
populateMenus(input, step.keys, getDisabledKeys(step));
// On mobile return the wrapper; the select is inside it.
// Change events still fire on the inner select.
return wrapNativeSelect(input);
}
input.on("chosen:showing_dropdown", function () {
$(this).data("wiktku-defaultValue", $(this).val());
});
input.change(function (_, context) {
var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this);
var oldValue = $(this).data("wiktku-defaultValue");
var newValue = context.selected;
inputs.find("option[value='" + oldValue + "']").prop('disabled', false);
inputs.find("option[value='" + newValue + "']").prop('disabled', true);
inputs.trigger("chosen:updated");
});
populateMenus(input, step.keys, getDisabledKeys(step));
return input;
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) nextKeyInput.chosen(chosenOptions);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var didFocus = false;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê ya kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
if (typeof(step.visible) === "function" && !step.visible(values)) {
$("#wiktku-start-" + stepId).slideUp();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
var didSlideDown;
if (!didFocus && stepDiv.css("display") === "none" && step.required) {
didSlideDown = function (evt) {
input.focus();
};
didFocus = true;
}
stepDiv.slideDown(didSlideDown);
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
continueBtn.setDisabled(!canContinue);
}
function destroy() {
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard, #editform").slideDown();
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and the navz pseudo-code.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections at all — just append.
var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : "";
return existing + sep + wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry[1] : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections separated by newlines.
var joined = sections.map(function (s) {
// Ensure each section ends with exactly one newline before the next.
return s.content.replace(/\n+$/, "");
}).join("\n");
return preamble + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê beşeke nû li rûpelê îlawe bikî. Eger ev zimanê ku tu îlawe dikî jixwe di rûpelê de hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bikin, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Naveroka rûpelê paqij bike (rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
spinner.show();
getWikitext(function (wikitext) {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page and start fresh — just
// write the newly generated wikitext as the full content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
$("#wiktku-preview-container").remove();
destroy();
});
});
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
stepDiv.append($(mw.html.element("h3", {
"class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", {
"for": "wiktku-start-" + step.id + "-field" }, name)))));
if (step.question_fmt) {
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, step.question_fmt)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
if (isMobile) {
stepDiv.append(wrapNativeSelect(input));
} else {
stepDiv.append(input);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append($("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Insert tab bar just before the first per-section step.
if (step.id === "pos") startForm.append(sectionTabsDiv);
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beş zêde bike",
disabled: true
});
addSectionBtn.on("click", function () {
saveCurrentSection();
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
var stepDiv = $("#wiktku-start-dfn");
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
var stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated");
});
renderSectionTabs();
refresh();
$("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300);
});
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
return;
}
sectionTabsDiv.show();
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection) return;
saveCurrentSection();
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
}
renderSectionTabs();
/**
* Restores a full cached draft: shared lang/pron fields, then every
* section (via the same restoreSection()/refresh()/renderSectionTabs()
* sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
mw.notify("Danûstandina berê ya tomarkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
renderSectionTabs();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
mw.notify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
// which is invisible on mobile and easy to miss on desktop.
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
framed: false
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load (see the
// ziman.json .then() callback below), once the full language list
// is available.
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache tomar bike",
title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
var snapshot = {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
sections: sections,
activeSection: activeSection
};
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
formCache.Set(snapshot);
mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" });
}
});
footer.append(saveCacheBtn.$element);
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
footer.append(clearCacheBtn.$element);
// ── Preview checkboxes ───────────────────────────────────────────────
// Wikitext preview (raw text panel)
var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>");
var previewLabel = $("<label for='wiktku-preview-toggle'></label>")
.text(" Pêşdîtina wîkîtekstê");
var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide();
// Live rendered preview (parsed HTML panel, same logic as AjaxEdit).
// Not available on mobile — API polling causes too many problems on small screens.
var livePreviewCheck = null;
var livePreviewPanel = null;
if (!isMobile) {
livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>");
livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide();
}
// Container that holds both panels and switches between stacked / side-by-side.
var previewContainer = $("<div id='wiktku-preview-container'></div>");
previewContainer.append(previewPanel);
if (!isMobile) previewContainer.append(livePreviewPanel);
var previewTimer = null;
var livePreviewIntervalId = -1;
var livePreviewActive = false; // guards in-flight fetches after uncheck
/** Rebuilds wikitext preview if its checkbox is checked. */
function schedulePreview() {
if (!previewCheck.prop("checked")) return;
clearTimeout(previewTimer);
// Debounce: wait 400 ms after the last keystroke before rebuilding.
previewTimer = setTimeout(function () {
getWikitext(function (wikitext) {
previewPanel.text(wikitext).show();
updatePanelLayout();
});
}, 400);
}
/** Fetches parsed HTML and populates the live preview panel. */
function loadLivePreview() {
getWikitext(function (wikitext) {
new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) {
// Discard result if the user unchecked while the fetch was in flight.
if (!livePreviewActive) return;
livePreviewPanel.html(html).show();
updatePanelLayout();
});
});
}
/** Adjusts panel widths depending on which previews are visible. */
function updatePanelLayout() {
var wikitextVisible = previewCheck.prop("checked");
var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked");
if (wikitextVisible && liveVisible) {
// Side by side — each panel takes roughly half the available width.
previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
} else {
// Full width for whichever is visible — only touch panels that are
// actually shown so that setting flex doesn't override display:none.
if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" });
if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" });
}
}
previewCheck.on("change", function () {
if ($(this).prop("checked")) {
schedulePreview();
} else {
clearTimeout(previewTimer);
previewPanel.hide();
updatePanelLayout();
}
});
if (!isMobile) {
livePreviewCheck.on("change", function () {
if ($(this).prop("checked")) {
livePreviewActive = true;
livePreviewPanel.show();
loadLivePreview();
livePreviewIntervalId = setInterval(loadLivePreview, 500);
} else {
livePreviewActive = false;
clearInterval(livePreviewIntervalId);
livePreviewIntervalId = -1;
livePreviewPanel.hide().empty();
updatePanelLayout();
}
});
}
// Listen on all input/change events on the form and update both previews.
startForm.on("input change", function () {
schedulePreview();
});
footer.append($("<span style='margin-left:1em'></span>")
.append(previewCheck).append(previewLabel));
if (!isMobile) {
var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>")
.text(" Pêşdîtina zindî");
footer.append($("<span style='margin-left:1em'></span>")
.append(livePreviewCheck).append(livePreviewLabel));
}
startForm.append(footer);
$("#editform").hide().before(startForm);
// Insert preview container (holds both panels) after the form.
startForm.after(previewContainer);
if (!isMobile) startForm.find("select").chosen(chosenOptions);
refresh();
// Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike.
// Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye
// tên derxistin. Zimanên xardkirî li serî dimînin, yên din
// bi alfabeyê li dû wan tên.
// Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices),
// already loaded into steps.lang.choices before build() runs.
var pinnedChoices = steps.lang.choices.slice();
var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; });
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-translation_editor.js/ziman.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var zimanData;
try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; }
var extraChoices = [];
$.each(zimanData, function (code, name) {
// Filter: skip proto-language codes and legendary/reconstructed langs.
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
// Skip codes already in the pinned list (they are handled above).
if (pinnedCodes.indexOf(code) !== -1) return;
// sortKey: bare name without leading "zimanê " for sorting.
var sortKey = name.replace(/^ziman[eê]\s+/i, "");
var choice = [code, name, sortKey];
extraChoices.push(choice);
langsByCode[code] = choice;
});
// Populate langsByCode for pinned choices too.
$.each(pinnedChoices, function (i, choice) {
// Ensure pinned choices have a sortKey (index 2).
if (choice.length < 3) choice[2] = choice[1];
langsByCode[choice[0]] = choice;
});
// Sort the extra choices alphabetically by sort key.
extraChoices.sort(function (a, b) {
return a[2].localeCompare(b[2], userLang);
});
// Final list: pinned first, then the rest.
var allChoices = pinnedChoices.concat(extraChoices);
steps.lang.choices = allChoices;
steps.trans.keys = allChoices;
steps.dunde.keys = allChoices;
steps.zarava.keys = getZaravaKeys(allChoices);
var langMenu = $("#wiktku-start-lang-field");
populateMenus(langMenu, steps.lang.choices);
langMenu.trigger("chosen:updated");
var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield");
populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans));
transMenus.trigger("chosen:updated");
var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield");
populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde));
dundeMenus.trigger("chosen:updated");
var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield");
populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava));
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page (if the "Li
// cache tomar bike" button was ever used here).
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
});
}
mw.loader.using(["jquery.chosen", "mediawiki.api", "oojs-ui-core"], function () {
if (!isMobile) {
mw.notify(
"Amûr dest pê dike, hinek bisekinin.<br><b>Not: jkx Sentaks (Syntax highlighting) neaktîv bikin</b>",
{ type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 }
);
}
// Load metadata, langChoices, posChoices and tplDefs from the companion
// data page so they can be updated without touching this script.
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-start.js/data.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var d;
try { d = JSON.parse(content); } catch (e) { d = {}; }
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Populate lang choices (pinned list used by build() → ziman.json loader).
if (d.langChoices) steps.lang.choices = d.langChoices;
// Populate POS choices.
if (d.posChoices) steps.pos.choices = d.posChoices;
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the (now populated) lang choices.
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
}).always(build);
});
});
//</nowiki>
mxbqc4skqbigr39z8eachfs3oqeihh4
6276932
6276931
2026-07-23T12:38:37Z
Wikihez
7344
([[m:Special:MyLanguage/User:Jon Harald Søby/diffedit|diffedit]])
6276932
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage)
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var action = mw.config.get("wgAction");
var pageExists = !!mw.config.get("wgArticleId");
// "Zimanek lê zêde bike" mode: triggered from the portlet link below.
var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1";
// On a normal page view of an existing page, add the portlet link
if (action === "view" && pageExists) {
var targetContainer;
var skin = mw.config.get('skin');
if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Ji vê rûpelê re ziman an jî beşeke nû zêde bike");
return;
}
if (!(action === "edit" || action === "submit")) {
return;
}
if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) {
return;
}
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
// Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra
// whitespace. codePattern: escaped code for one language, or "[^}]+" for any.
function langHeaderSource(codePattern) {
return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Finds a language's header in wikitext (see langHeaderSource). Returns
// { index, match } or null.
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
// Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the
// "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched
// once on first use and cached — every citation row's picker shares it.
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10, // navê-navî Şablon:, her tim 10 ye
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" (an hevwateya wê ya herêmî) rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
// Draft cache for this page — lets the editor manually save/restore the
// whole wizard state (shared lang/pron + every section) via
// [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of
// any single browser session. Keyed per page so different articles'
// drafts don't collide.
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
// Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check.
var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches;
var chosenOptions = {
width: "20em",
disable_search_threshold: 0,
placeholder_text_single: "Yekî hilbijêre",
placeholder_text_multiple: "Hilbijêre",
no_results_text: "Nehat dîtin",
enable_split_word_search: true,
search_contains: true };
function wrapNativeSelect(select) {
var placeholder = select.attr("data-placeholder") || "Lê bigere…";
// Hide the native select — we drive it programmatically.
select.hide();
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide();
// Full option cache — rebuilt when chosen:updated fires.
var allOptions = [];
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
if (!v) return; // skip the empty placeholder option
allOptions.push({
value: v,
text: $(this).text(),
disabled: $(this).prop("disabled")
});
});
}
cacheOptions();
function showResults(query) {
var q = query.trim().toLowerCase();
resultsList.empty();
var matched;
if (q) {
matched = [];
$.each(allOptions, function (_, o) {
if (o.disabled) return;
var codeMatch = o.value.toLowerCase().indexOf(q) !== -1;
var nameMatch = o.text.toLowerCase().indexOf(q) !== -1;
if (codeMatch || nameMatch) {
matched.push({ opt: o, codeMatch: codeMatch });
}
});
// Code matches (especially prefix matches) come first.
matched.sort(function (a, b) {
var aCode = a.opt.value.toLowerCase();
var bCode = b.opt.value.toLowerCase();
var aPrefix = aCode.indexOf(q) === 0;
var bPrefix = bCode.indexOf(q) === 0;
if (aPrefix !== bPrefix) return aPrefix ? -1 : 1;
if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1;
return 0;
});
matched = matched.map(function (item) { return item.opt; });
} else {
matched = allOptions.filter(function (o) { return !o.disabled; });
}
if (!matched.length) {
resultsList.hide();
return;
}
$.each(matched.slice(0, 50), function (_, opt) {
var li = $("<li></li>").text(opt.text).attr("data-value", opt.value);
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// Keep mousedown for desktop fallback.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
resultsList.append(li);
});
// Position the list above or below depending on available viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px ≈ 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
resultsList.show();
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
resultsList.hide();
select.trigger("change");
}
textInput.on("input focus", function () {
showResults($(this).val());
});
textInput.on("blur", function () {
// Short delay so a touchstart on a result item fires first.
setTimeout(function () { resultsList.hide(); }, 200);
});
// chosen:updated → re-cache (called after ziman.json loads).
select.on("chosen:updated", function () {
cacheOptions();
// If a value is already selected, keep the display text in sync.
var cur = select.val();
if (cur) {
var match = $.grep(allOptions, function (o) { return o.value === cur; })[0];
if (match) textInput.val(match.text);
}
});
// No-op .chosen() so desktop call-sites don't need guarding.
select.chosen = function () { return select; };
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
var langsByCode = {};
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
// Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin —
// ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin.
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c[0]] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a
// template-based sense has no "own" gloss for alternate forms, synonyms,
// antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket,
// Nimûne and Jêgirtin stay visible either way.
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{bênimûne|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = text.replace(
new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"),
"'''$1'''");
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + n.tr;
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + n.t;
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 200 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) out += "|tercime=" + jeder.quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
function makeWordListStep(id, name, question_fmt, header, templateName) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
if (words.length === 1) {
// Single word — keep the simple inline template.
return section +
"* {{" + templateName + "|" + lang + "|" +
words[0] + "}}\n";
}
// Multiple words — use {{kol3}} with one positional param per word.
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
} },
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: [],
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
}
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line.charAt(0) === "*" ? line : "* " + line;
}).join("\n");
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var cinsiyet = values.cinsiyet || ""; // from the cinsiyet step
var lekTur = values.lektur || ""; // from the lêker transitivity step
var capitalizedPos = capitalize(pos);
var outputString = "\n=== " + capitalizedPos + " ===\n";
// Add ku-tewîn-nav for single-word navdêr/serenav with known gender.
if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] &&
(pos === "navdêr" || pos === "serenav") && values.lang === "ku") {
outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet];
if (pos === "serenav") outputString += "|y";
outputString += "}}\n";
}
outputString += "{{" + pos + "|" + values.lang;
if (pos === "navdêr" || pos === "serenav") {
if (cinsiyet) {
outputString += "|c=" + cinsiyet;
} else if (values.lang === "ku") {
// Unknown gender — prompt editor to fill it in.
outputString += "|c=?";
}
} else if (/^lêker/.test(pos) && lekTur) {
outputString += "|c=" + lekTur;
}
if (values.tr) {
outputString += "|tr=" + values.tr;
}
outputString += "}}\n";
return outputString;
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function (values) {
var meta = metadata[values.lang];
return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") &&
meta && meta.g && meta.g.length);
},
build: function (stepDiv, values) {
var select = stepDiv.find("select.wiktku-start-step-field");
if (!select.length) {
select = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-cinsiyet-field" }));
stepDiv.append(select);
}
if (!values) return select;
var meta = metadata[values.lang];
var codes = (meta && meta.g) || [];
var prevVal = select.val();
var choices = $.map(codes, function (code) {
return [[code, genderLabel(code)]];
});
populateMenus(select, choices);
if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) {
select.val(prevVal);
}
if (!isMobile) select.trigger("chosen:updated");
return select;
},
output: function () { return ""; } // consumed by pos.output
},
// Lêker transitivity — shown for any lêker variant.
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function (values) {
return values.lang && values.pos && /^lêker/.test(values.pos);
},
// Dictionary: value → display label. Add new types here.
choices: [
["gh", "gerguhêz"],
['x-gh', 'xwerû-gerguhêz'],
['p-gh', 'pêkhatî-gerguhêz'],
['h-gh', 'hevedudanî-gerguhêz'],
['b-gh', 'biwêjî-gerguhêz'],
["ngh", "negerguhêz"],
['x-ngh', 'xwerû-negerguhêz'],
['p-ngh', 'pêkhatî-negerguhêz'],
['h-ngh', 'hevedudanî-negerguhêz'],
['b-ngh', 'biwêjî-negerguhêz'] ],
output: function () { return ""; } // consumed by pos.output
},
tr: {
id: "tr",
name: "transkrîpsiyon",
question_fmt: "Transkrîpsiyona kelîmeyê",
multiple: false,
narrow: true,
visible: function (values) {
return values.lang && values.pos && needsTransliteration(values.lang);
} },
dfn: {
id: "dfn",
// required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne
name: "Mane",
question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!",
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function (stepDiv) {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
dfnArea.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || [];
// Cache of all non-placeholder option elements — used by updateTplOptions
// to remove/re-add options (the only approach compatible with Chosen.js).
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (_, tpl) {
var opt = $("<option></option>")
.val(tpl.name)
.text(tpl.name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible ones.
// This is the only approach that works correctly with both native <select>
// and Chosen.js on desktop.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
if (!isMobile) tplSelect.trigger("chosen:updated");
if (currentVal && !currentStillValid) {
tplSelect.val("");
dfnArea.show();
tplTitleInput.hide().val("");
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpelê…")
.hide();
// Grow the sense list when the title field of the last block gets content,
// mirroring what onDfnInput does for the plain mane textarea.
tplTitleInput.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after programmatically setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
tplTitleInput.show().focus();
} else {
dfnArea.show();
tplTitleInput.hide().val("");
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput);
block.append(tplRow);
block.append(dfnArea);
// Apply lang filter on initial build.
updateTplOptions();
// — Sub-field groups —
// All groups are always visible with a placeholder textarea.
// Nimûne is last in the DOM and last in the output.
var allGroups = [
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in" },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" },
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": ");
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": " + g.izah);
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", g.label + "…");
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow);
});
list.append($("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder (citation) group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text("Jêgirtin: ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
var dragHandle = $("<span></span>")
.addClass("wiktku-jeder-drag-handle")
.attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(dragHandle).append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<input type='text'>")
.addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow")
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi Citoid bîne",
classes: [ "wiktku-dfn-quote-citoid-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoidHelper.ensure().then(function () {
return wikt.citoid.fetchJederFields(query);
}).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
srcSel.trigger("chosen:updated");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
if (!chosen) return;
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
// so the saved value is always exactly what's in that
// (editable) textarea — no separate hidden field that can
// end up empty and silently fall back to "çavkanî?".
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Navê şablonê lê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
function showCatResults(query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
}
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
function selectCatTpl(name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
}
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (_, f) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(f.label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", f.key)
.attr("placeholder", f.label + "…");
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
// Make citation rows sortable (drag to reorder).
// The last row is always the empty trailing row — cancel its drag.
mw.loader.using("jquery.ui.sortable", function () {
jederList.sortable({
handle: ".wiktku-jeder-drag-handle",
axis: "y",
items: "li:not(:last-child)",
tolerance: "pointer"
});
});
jederGroupDiv.append(jederLbl).append(jederList);
block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
return block;
},
/** Grows the sense list when the last mane textarea gets content. */
onDfnInput: function (stepDiv, block, textarea) {
var val = $.trim($(textarea).val());
var list = stepDiv.find("ol.wiktku-dfn-list");
var isLast = block.is(list.children("li:last-child"));
if (val && isLast) {
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
// Apply Chosen to the new block's template selector on desktop.
if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions);
}
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow);
});
list.append($("<li></li>").append(next));
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: [],
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
// True only when at least one sense has a regular mane (needs linkify).
var hasRegularMane = senses.some(function (s) {
return !s.tpl && s.mane !== "";
});
// nimûne/jêder must never pass through linkify, only the mane text.
// defLines (linkified) and citeLines (kept aside) stay parallel;
// a {{wiktku-cite-N}} token in defLines marks where to splice back.
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
$.each(senses, function (_, sense) {
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
var idx = defLines.length;
var cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
if (dfn) {
dfn = stripWikilinks(dfn);
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
} else {
dfn = "{{mane?|" + lang + "}}";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl : "") + (labelTpl ? labelTpl : "") + dfn;
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
var cite = buildCite(sense, lang);
var idx = defLines.length;
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with bênimûne.
text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n";
callback(text);
return;
}
if (!hasRegularMane) {
// All filled senses are template senses — no linkify needed.
callback(restoreCites(text));
return;
}
var didTryToLoad = function () {
if (typeof $.wiktviLinkify === "function") {
$.wiktviLinkify(text, function (linked) {
callback(restoreCites(linked));
});
} else {
callback(restoreCites(text));
}
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"),
binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: [],
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls array.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder,
// Only when a regular sense has content (real mane, or {{mane?}}
// with other fields filled in) — a şablon sense never counts.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (!hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: {
"navz": true},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {
"navz": true
},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
if (!wordLines) return undefined;
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it — nothing to output, and gloss.text is
// empty so there's no caption to give {{werger-ser}} anyway.
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din ên (ne jêgirtinan) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji jêrê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
if (!isMobile) sel.trigger("chosen:updated");
ta.focus();
});
wrap.append(sel).append(status).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
var ordered_steps = [
steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr,
steps.dfn, steps.je, steps.tekildar,
steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani];
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "cinsiyet", "lektur", "tr",
"dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing list so build() recreates it cleanly.
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set programmatically (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true);
});
subList.append($("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text);
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote);
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Notify chosen widgets of the programmatic value change.
if (!isMobile) fields.filter("select").trigger("chosen:updated");
});
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang/pron header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var entry = steps.lang.output(sharedValues);
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) { return s.mane; }).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>"));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice[1]);
if (choice[2] !== undefined) {
// This is a language choice [code, name, sortKey] — show code.
displayText = capitalize(choice[1]) + " (" + choice[0] + ")";
}
var option = $(mw.html.element("option", {
value: choice[0] }, displayText));
if (disabledValues[choice[0]]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
if (isMobile) {
populateMenus(input, step.keys, getDisabledKeys(step));
// On mobile return the wrapper; the select is inside it.
// Change events still fire on the inner select.
return wrapNativeSelect(input);
}
input.on("chosen:showing_dropdown", function () {
$(this).data("wiktku-defaultValue", $(this).val());
});
input.change(function (_, context) {
var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this);
var oldValue = $(this).data("wiktku-defaultValue");
var newValue = context.selected;
inputs.find("option[value='" + oldValue + "']").prop('disabled', false);
inputs.find("option[value='" + newValue + "']").prop('disabled', true);
inputs.trigger("chosen:updated");
});
populateMenus(input, step.keys, getDisabledKeys(step));
return input;
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) nextKeyInput.chosen(chosenOptions);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var didFocus = false;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê ya kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
if (typeof(step.visible) === "function" && !step.visible(values)) {
$("#wiktku-start-" + stepId).slideUp();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
var didSlideDown;
if (!didFocus && stepDiv.css("display") === "none" && step.required) {
didSlideDown = function (evt) {
input.focus();
};
didFocus = true;
}
stepDiv.slideDown(didSlideDown);
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
continueBtn.setDisabled(!canContinue);
}
function destroy() {
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard, #editform").slideDown();
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and the navz pseudo-code.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections at all — just append.
var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : "";
return existing + sep + wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry[1] : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections separated by newlines.
var joined = sections.map(function (s) {
// Ensure each section ends with exactly one newline before the next.
return s.content.replace(/\n+$/, "");
}).join("\n");
return preamble + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê beşeke nû li rûpelê îlawe bikî. Eger ev zimanê ku tu îlawe dikî jixwe di rûpelê de hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bikin, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Naveroka rûpelê paqij bike (rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
spinner.show();
getWikitext(function (wikitext) {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page and start fresh — just
// write the newly generated wikitext as the full content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
$("#wiktku-preview-container").remove();
destroy();
});
});
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
stepDiv.append($(mw.html.element("h3", {
"class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", {
"for": "wiktku-start-" + step.id + "-field" }, name)))));
if (step.question_fmt) {
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, step.question_fmt)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
if (isMobile) {
stepDiv.append(wrapNativeSelect(input));
} else {
stepDiv.append(input);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append($("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Insert tab bar just before the first per-section step.
if (step.id === "pos") startForm.append(sectionTabsDiv);
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beş zêde bike",
disabled: true
});
addSectionBtn.on("click", function () {
saveCurrentSection();
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
var stepDiv = $("#wiktku-start-dfn");
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
var stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated");
});
renderSectionTabs();
refresh();
$("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300);
});
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
return;
}
sectionTabsDiv.show();
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection) return;
saveCurrentSection();
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
}
renderSectionTabs();
/**
* Restores a full cached draft: shared lang/pron fields, then every
* section (via the same restoreSection()/refresh()/renderSectionTabs()
* sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
mw.notify("Danûstandina berê ya tomarkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
renderSectionTabs();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
mw.notify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
// which is invisible on mobile and easy to miss on desktop.
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
framed: false
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load (see the
// ziman.json .then() callback below), once the full language list
// is available.
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache tomar bike",
title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
var snapshot = {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
sections: sections,
activeSection: activeSection
};
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
formCache.Set(snapshot);
mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" });
}
});
footer.append(saveCacheBtn.$element);
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
footer.append(clearCacheBtn.$element);
// ── Preview checkboxes ───────────────────────────────────────────────
// Wikitext preview (raw text panel)
var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>");
var previewLabel = $("<label for='wiktku-preview-toggle'></label>")
.text(" Pêşdîtina wîkîtekstê");
var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide();
// Live rendered preview (parsed HTML panel, same logic as AjaxEdit).
// Not available on mobile — API polling causes too many problems on small screens.
var livePreviewCheck = null;
var livePreviewPanel = null;
if (!isMobile) {
livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>");
livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide();
}
// Container that holds both panels and switches between stacked / side-by-side.
var previewContainer = $("<div id='wiktku-preview-container'></div>");
previewContainer.append(previewPanel);
if (!isMobile) previewContainer.append(livePreviewPanel);
var previewTimer = null;
var livePreviewIntervalId = -1;
var livePreviewActive = false; // guards in-flight fetches after uncheck
/** Rebuilds wikitext preview if its checkbox is checked. */
function schedulePreview() {
if (!previewCheck.prop("checked")) return;
clearTimeout(previewTimer);
// Debounce: wait 400 ms after the last keystroke before rebuilding.
previewTimer = setTimeout(function () {
getWikitext(function (wikitext) {
previewPanel.text(wikitext).show();
updatePanelLayout();
});
}, 400);
}
/** Fetches parsed HTML and populates the live preview panel. */
function loadLivePreview() {
getWikitext(function (wikitext) {
new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) {
// Discard result if the user unchecked while the fetch was in flight.
if (!livePreviewActive) return;
livePreviewPanel.html(html).show();
updatePanelLayout();
});
});
}
/** Adjusts panel widths depending on which previews are visible. */
function updatePanelLayout() {
var wikitextVisible = previewCheck.prop("checked");
var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked");
if (wikitextVisible && liveVisible) {
// Side by side — each panel takes roughly half the available width.
previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
} else {
// Full width for whichever is visible — only touch panels that are
// actually shown so that setting flex doesn't override display:none.
if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" });
if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" });
}
}
previewCheck.on("change", function () {
if ($(this).prop("checked")) {
schedulePreview();
} else {
clearTimeout(previewTimer);
previewPanel.hide();
updatePanelLayout();
}
});
if (!isMobile) {
livePreviewCheck.on("change", function () {
if ($(this).prop("checked")) {
livePreviewActive = true;
livePreviewPanel.show();
loadLivePreview();
livePreviewIntervalId = setInterval(loadLivePreview, 500);
} else {
livePreviewActive = false;
clearInterval(livePreviewIntervalId);
livePreviewIntervalId = -1;
livePreviewPanel.hide().empty();
updatePanelLayout();
}
});
}
// Listen on all input/change events on the form and update both previews.
startForm.on("input change", function () {
schedulePreview();
});
footer.append($("<span style='margin-left:1em'></span>")
.append(previewCheck).append(previewLabel));
if (!isMobile) {
var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>")
.text(" Pêşdîtina zindî");
footer.append($("<span style='margin-left:1em'></span>")
.append(livePreviewCheck).append(livePreviewLabel));
}
startForm.append(footer);
$("#editform").hide().before(startForm);
// Insert preview container (holds both panels) after the form.
startForm.after(previewContainer);
if (!isMobile) startForm.find("select").chosen(chosenOptions);
refresh();
// Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike.
// Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye
// tên derxistin. Zimanên xardkirî li serî dimînin, yên din
// bi alfabeyê li dû wan tên.
// Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices),
// already loaded into steps.lang.choices before build() runs.
var pinnedChoices = steps.lang.choices.slice();
var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; });
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-translation_editor.js/ziman.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var zimanData;
try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; }
var extraChoices = [];
$.each(zimanData, function (code, name) {
// Filter: skip proto-language codes and legendary/reconstructed langs.
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
// Skip codes already in the pinned list (they are handled above).
if (pinnedCodes.indexOf(code) !== -1) return;
// sortKey: bare name without leading "zimanê " for sorting.
var sortKey = name.replace(/^ziman[eê]\s+/i, "");
var choice = [code, name, sortKey];
extraChoices.push(choice);
langsByCode[code] = choice;
});
// Populate langsByCode for pinned choices too.
$.each(pinnedChoices, function (i, choice) {
// Ensure pinned choices have a sortKey (index 2).
if (choice.length < 3) choice[2] = choice[1];
langsByCode[choice[0]] = choice;
});
// Sort the extra choices alphabetically by sort key.
extraChoices.sort(function (a, b) {
return a[2].localeCompare(b[2], userLang);
});
// Final list: pinned first, then the rest.
var allChoices = pinnedChoices.concat(extraChoices);
steps.lang.choices = allChoices;
steps.trans.keys = allChoices;
steps.dunde.keys = allChoices;
steps.zarava.keys = getZaravaKeys(allChoices);
var langMenu = $("#wiktku-start-lang-field");
populateMenus(langMenu, steps.lang.choices);
langMenu.trigger("chosen:updated");
var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield");
populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans));
transMenus.trigger("chosen:updated");
var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield");
populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde));
dundeMenus.trigger("chosen:updated");
var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield");
populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava));
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page (if the "Li
// cache tomar bike" button was ever used here).
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
});
}
mw.loader.using(["jquery.chosen", "mediawiki.api", "oojs-ui-core"], function () {
if (!isMobile) {
mw.notify(
"Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin",
{ type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 }
);
}
// Load metadata, langChoices, posChoices and tplDefs from the companion
// data page so they can be updated without touching this script.
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-start.js/data.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var d;
try { d = JSON.parse(content); } catch (e) { d = {}; }
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Populate lang choices (pinned list used by build() → ziman.json loader).
if (d.langChoices) steps.lang.choices = d.langChoices;
// Populate POS choices.
if (d.posChoices) steps.pos.choices = d.posChoices;
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the (now populated) lang choices.
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
}).always(build);
});
});
//</nowiki>
7xlsk0sprzrtqxvsgdol57qq8o1u0h4
6276936
6276932
2026-07-23T12:57:47Z
Wikihez
7344
6276936
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage)
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var action = mw.config.get("wgAction");
var pageExists = !!mw.config.get("wgArticleId");
// "Zimanek lê zêde bike" mode: triggered from the portlet link below.
var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1";
// On a normal page view of an existing page, add the portlet link
if (action === "view" && pageExists) {
var targetContainer;
var skin = mw.config.get('skin');
if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Ji vê rûpelê re ziman an jî beşeke nû zêde bike");
return;
}
if (!(action === "edit" || action === "submit")) {
return;
}
if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) {
return;
}
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
// Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra
// whitespace. codePattern: escaped code for one language, or "[^}]+" for any.
function langHeaderSource(codePattern) {
return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Finds a language's header in wikitext (see langHeaderSource). Returns
// { index, match } or null.
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
// Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the
// "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched
// once on first use and cached — every citation row's picker shares it.
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10, // navê-navî Şablon:, her tim 10 ye
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" (an hevwateya wê ya herêmî) rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
// Draft cache for this page — lets the editor manually save/restore the
// whole wizard state (shared lang/pron + every section) via
// [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of
// any single browser session. Keyed per page so different articles'
// drafts don't collide.
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
// Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check.
var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches;
var chosenOptions = {
width: "20em",
disable_search_threshold: 0,
placeholder_text_single: "Yekî hilbijêre",
placeholder_text_multiple: "Hilbijêre",
no_results_text: "Nehat dîtin",
enable_split_word_search: true,
search_contains: true };
function wrapNativeSelect(select) {
var placeholder = select.attr("data-placeholder") || "Lê bigere…";
// Hide the native select — we drive it programmatically.
select.hide();
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide();
// Full option cache — rebuilt when chosen:updated fires.
var allOptions = [];
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
if (!v) return; // skip the empty placeholder option
allOptions.push({
value: v,
text: $(this).text(),
disabled: $(this).prop("disabled")
});
});
}
cacheOptions();
function showResults(query) {
var q = query.trim().toLowerCase();
resultsList.empty();
var matched;
if (q) {
matched = [];
$.each(allOptions, function (_, o) {
if (o.disabled) return;
var codeMatch = o.value.toLowerCase().indexOf(q) !== -1;
var nameMatch = o.text.toLowerCase().indexOf(q) !== -1;
if (codeMatch || nameMatch) {
matched.push({ opt: o, codeMatch: codeMatch });
}
});
// Code matches (especially prefix matches) come first.
matched.sort(function (a, b) {
var aCode = a.opt.value.toLowerCase();
var bCode = b.opt.value.toLowerCase();
var aPrefix = aCode.indexOf(q) === 0;
var bPrefix = bCode.indexOf(q) === 0;
if (aPrefix !== bPrefix) return aPrefix ? -1 : 1;
if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1;
return 0;
});
matched = matched.map(function (item) { return item.opt; });
} else {
matched = allOptions.filter(function (o) { return !o.disabled; });
}
if (!matched.length) {
resultsList.hide();
return;
}
$.each(matched.slice(0, 50), function (_, opt) {
var li = $("<li></li>").text(opt.text).attr("data-value", opt.value);
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// Keep mousedown for desktop fallback.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
resultsList.append(li);
});
// Position the list above or below depending on available viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px ≈ 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
resultsList.show();
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
resultsList.hide();
select.trigger("change");
}
textInput.on("input focus", function () {
showResults($(this).val());
});
textInput.on("blur", function () {
// Short delay so a touchstart on a result item fires first.
setTimeout(function () { resultsList.hide(); }, 200);
});
// chosen:updated → re-cache (called after ziman.json loads).
select.on("chosen:updated", function () {
cacheOptions();
// If a value is already selected, keep the display text in sync.
var cur = select.val();
if (cur) {
var match = $.grep(allOptions, function (o) { return o.value === cur; })[0];
if (match) textInput.val(match.text);
}
});
// No-op .chosen() so desktop call-sites don't need guarding.
select.chosen = function () { return select; };
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
var langsByCode = {};
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
// Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin —
// ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin.
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c[0]] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a
// template-based sense has no "own" gloss for alternate forms, synonyms,
// antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket,
// Nimûne and Jêgirtin stay visible either way.
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{bênimûne|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = text.replace(
new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"),
"'''$1'''");
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + n.tr;
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + n.t;
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 200 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) out += "|tercime=" + jeder.quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
function makeWordListStep(id, name, question_fmt, header, templateName) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
if (words.length === 1) {
// Single word — keep the simple inline template.
return section +
"* {{" + templateName + "|" + lang + "|" +
words[0] + "}}\n";
}
// Multiple words — use {{kol3}} with one positional param per word.
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
} },
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: [],
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
}
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line.charAt(0) === "*" ? line : "* " + line;
}).join("\n");
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var cinsiyet = values.cinsiyet || ""; // from the cinsiyet step
var lekTur = values.lektur || ""; // from the lêker transitivity step
var capitalizedPos = capitalize(pos);
var outputString = "\n=== " + capitalizedPos + " ===\n";
// Add ku-tewîn-nav for single-word navdêr/serenav with known gender.
if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] &&
(pos === "navdêr" || pos === "serenav") && values.lang === "ku") {
outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet];
if (pos === "serenav") outputString += "|y";
outputString += "}}\n";
}
outputString += "{{" + pos + "|" + values.lang;
if (pos === "navdêr" || pos === "serenav") {
if (cinsiyet) {
outputString += "|c=" + cinsiyet;
} else if (values.lang === "ku") {
// Unknown gender — prompt editor to fill it in.
outputString += "|c=?";
}
} else if (/^lêker/.test(pos) && lekTur) {
outputString += "|c=" + lekTur;
}
if (values.tr) {
outputString += "|tr=" + values.tr;
}
outputString += "}}\n";
return outputString;
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function (values) {
var meta = metadata[values.lang];
return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") &&
meta && meta.g && meta.g.length);
},
build: function (stepDiv, values) {
var select = stepDiv.find("select.wiktku-start-step-field");
if (!select.length) {
select = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-cinsiyet-field" }));
stepDiv.append(select);
}
if (!values) return select;
var meta = metadata[values.lang];
var codes = (meta && meta.g) || [];
var prevVal = select.val();
var choices = $.map(codes, function (code) {
return [[code, genderLabel(code)]];
});
populateMenus(select, choices);
if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) {
select.val(prevVal);
}
if (!isMobile) select.trigger("chosen:updated");
return select;
},
output: function () { return ""; } // consumed by pos.output
},
// Lêker transitivity — shown for any lêker variant.
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function (values) {
return values.lang && values.pos && /^lêker/.test(values.pos);
},
// Dictionary: value → display label. Add new types here.
choices: [
["gh", "gerguhêz"],
['x-gh', 'xwerû-gerguhêz'],
['p-gh', 'pêkhatî-gerguhêz'],
['h-gh', 'hevedudanî-gerguhêz'],
['b-gh', 'biwêjî-gerguhêz'],
["ngh", "negerguhêz"],
['x-ngh', 'xwerû-negerguhêz'],
['p-ngh', 'pêkhatî-negerguhêz'],
['h-ngh', 'hevedudanî-negerguhêz'],
['b-ngh', 'biwêjî-negerguhêz'] ],
output: function () { return ""; } // consumed by pos.output
},
tr: {
id: "tr",
name: "transkrîpsiyon",
question_fmt: "Transkrîpsiyona kelîmeyê",
multiple: false,
narrow: true,
visible: function (values) {
return values.lang && values.pos && needsTransliteration(values.lang);
} },
dfn: {
id: "dfn",
// required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne
name: "Mane",
question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!",
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function (stepDiv) {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
dfnArea.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || [];
// Cache of all non-placeholder option elements — used by updateTplOptions
// to remove/re-add options (the only approach compatible with Chosen.js).
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (_, tpl) {
var opt = $("<option></option>")
.val(tpl.name)
.text(tpl.name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible ones.
// This is the only approach that works correctly with both native <select>
// and Chosen.js on desktop.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
if (!isMobile) tplSelect.trigger("chosen:updated");
if (currentVal && !currentStillValid) {
tplSelect.val("");
dfnArea.show();
tplTitleInput.hide().val("");
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpelê…")
.hide();
// Grow the sense list when the title field of the last block gets content,
// mirroring what onDfnInput does for the plain mane textarea.
tplTitleInput.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after programmatically setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
tplTitleInput.show().focus();
} else {
dfnArea.show();
tplTitleInput.hide().val("");
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput);
block.append(tplRow);
block.append(dfnArea);
// Apply lang filter on initial build.
updateTplOptions();
// — Sub-field groups —
// All groups are always visible with a placeholder textarea.
// Nimûne is last in the DOM and last in the output.
var allGroups = [
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in" },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" },
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": ");
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": " + g.izah);
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", g.label + "…");
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow);
});
list.append($("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder (citation) group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text("Jêgirtin: ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
var dragHandle = $("<span></span>")
.addClass("wiktku-jeder-drag-handle")
.attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(dragHandle).append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<input type='text'>")
.addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow")
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi Citoid bîne",
classes: [ "wiktku-dfn-quote-citoid-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoidHelper.ensure().then(function () {
return wikt.citoid.fetchJederFields(query);
}).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
srcSel.trigger("chosen:updated");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
if (!chosen) return;
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
// so the saved value is always exactly what's in that
// (editable) textarea — no separate hidden field that can
// end up empty and silently fall back to "çavkanî?".
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Navê şablonê lê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
function showCatResults(query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
}
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
function selectCatTpl(name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
}
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (_, f) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(f.label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", f.key)
.attr("placeholder", f.label + "…");
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
// Make citation rows sortable (drag to reorder).
// The last row is always the empty trailing row — cancel its drag.
mw.loader.using("jquery.ui.sortable", function () {
jederList.sortable({
handle: ".wiktku-jeder-drag-handle",
axis: "y",
items: "li:not(:last-child)",
tolerance: "pointer"
});
});
jederGroupDiv.append(jederLbl).append(jederList);
block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
return block;
},
/** Grows the sense list when the last mane textarea gets content. */
onDfnInput: function (stepDiv, block, textarea) {
var val = $.trim($(textarea).val());
var list = stepDiv.find("ol.wiktku-dfn-list");
var isLast = block.is(list.children("li:last-child"));
if (val && isLast) {
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
// Apply Chosen to the new block's template selector on desktop.
if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions);
}
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow);
});
list.append($("<li></li>").append(next));
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: [],
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
// True only when at least one sense has a regular mane (needs linkify).
var hasRegularMane = senses.some(function (s) {
return !s.tpl && s.mane !== "";
});
// nimûne/jêder must never pass through linkify, only the mane text.
// defLines (linkified) and citeLines (kept aside) stay parallel;
// a {{wiktku-cite-N}} token in defLines marks where to splice back.
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
$.each(senses, function (_, sense) {
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
var idx = defLines.length;
var cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
if (dfn) {
dfn = stripWikilinks(dfn);
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
} else {
dfn = "{{mane?|" + lang + "}}";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
var cite = buildCite(sense, lang);
var idx = defLines.length;
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with bênimûne.
text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n";
callback(text);
return;
}
if (!hasRegularMane) {
// All filled senses are template senses — no linkify needed.
callback(restoreCites(text));
return;
}
var didTryToLoad = function () {
if (typeof $.wiktviLinkify === "function") {
$.wiktviLinkify(text, function (linked) {
callback(restoreCites(linked));
});
} else {
callback(restoreCites(text));
}
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"),
binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: [],
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls array.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder,
// Only when a regular sense has content (real mane, or {{mane?}}
// with other fields filled in) — a şablon sense never counts.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (!hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: {
"navz": true},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {
"navz": true
},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
if (!wordLines) return undefined;
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it — nothing to output, and gloss.text is
// empty so there's no caption to give {{werger-ser}} anyway.
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din ên (ne jêgirtinan) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji jêrê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
if (!isMobile) sel.trigger("chosen:updated");
ta.focus();
});
wrap.append(sel).append(status).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
var ordered_steps = [
steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr,
steps.dfn, steps.je, steps.tekildar,
steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani];
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "cinsiyet", "lektur", "tr",
"dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing list so build() recreates it cleanly.
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set programmatically (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true);
});
subList.append($("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text);
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote);
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Notify chosen widgets of the programmatic value change.
if (!isMobile) fields.filter("select").trigger("chosen:updated");
});
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang/pron header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var entry = steps.lang.output(sharedValues);
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) { return s.mane; }).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>"));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice[1]);
if (choice[2] !== undefined) {
// This is a language choice [code, name, sortKey] — show code.
displayText = capitalize(choice[1]) + " (" + choice[0] + ")";
}
var option = $(mw.html.element("option", {
value: choice[0] }, displayText));
if (disabledValues[choice[0]]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
if (isMobile) {
populateMenus(input, step.keys, getDisabledKeys(step));
// On mobile return the wrapper; the select is inside it.
// Change events still fire on the inner select.
return wrapNativeSelect(input);
}
input.on("chosen:showing_dropdown", function () {
$(this).data("wiktku-defaultValue", $(this).val());
});
input.change(function (_, context) {
var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this);
var oldValue = $(this).data("wiktku-defaultValue");
var newValue = context.selected;
inputs.find("option[value='" + oldValue + "']").prop('disabled', false);
inputs.find("option[value='" + newValue + "']").prop('disabled', true);
inputs.trigger("chosen:updated");
});
populateMenus(input, step.keys, getDisabledKeys(step));
return input;
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) nextKeyInput.chosen(chosenOptions);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var didFocus = false;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê ya kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
if (typeof(step.visible) === "function" && !step.visible(values)) {
$("#wiktku-start-" + stepId).slideUp();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
var didSlideDown;
if (!didFocus && stepDiv.css("display") === "none" && step.required) {
didSlideDown = function (evt) {
input.focus();
};
didFocus = true;
}
stepDiv.slideDown(didSlideDown);
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
continueBtn.setDisabled(!canContinue);
}
function destroy() {
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard, #editform").slideDown();
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and the navz pseudo-code.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections at all — just append.
var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : "";
return existing + sep + wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry[1] : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections separated by newlines.
var joined = sections.map(function (s) {
// Ensure each section ends with exactly one newline before the next.
return s.content.replace(/\n+$/, "");
}).join("\n");
return preamble + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê beşeke nû li rûpelê îlawe bikî. Eger ev zimanê ku tu îlawe dikî jixwe di rûpelê de hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bikin, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Naveroka rûpelê paqij bike (rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
spinner.show();
getWikitext(function (wikitext) {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page and start fresh — just
// write the newly generated wikitext as the full content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
$("#wiktku-preview-container").remove();
destroy();
});
});
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
stepDiv.append($(mw.html.element("h3", {
"class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", {
"for": "wiktku-start-" + step.id + "-field" }, name)))));
if (step.question_fmt) {
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, step.question_fmt)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
if (isMobile) {
stepDiv.append(wrapNativeSelect(input));
} else {
stepDiv.append(input);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append($("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Insert tab bar just before the first per-section step.
if (step.id === "pos") startForm.append(sectionTabsDiv);
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beş zêde bike",
disabled: true
});
addSectionBtn.on("click", function () {
saveCurrentSection();
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
var stepDiv = $("#wiktku-start-dfn");
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
var stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated");
});
renderSectionTabs();
refresh();
$("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300);
});
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
return;
}
sectionTabsDiv.show();
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection) return;
saveCurrentSection();
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
}
renderSectionTabs();
/**
* Restores a full cached draft: shared lang/pron fields, then every
* section (via the same restoreSection()/refresh()/renderSectionTabs()
* sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
mw.notify("Danûstandina berê ya tomarkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
renderSectionTabs();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
mw.notify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
// which is invisible on mobile and easy to miss on desktop.
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
framed: false
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load (see the
// ziman.json .then() callback below), once the full language list
// is available.
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache tomar bike",
title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
var snapshot = {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
sections: sections,
activeSection: activeSection
};
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
formCache.Set(snapshot);
mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" });
}
});
footer.append(saveCacheBtn.$element);
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
footer.append(clearCacheBtn.$element);
// ── Preview checkboxes ───────────────────────────────────────────────
// Wikitext preview (raw text panel)
var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>");
var previewLabel = $("<label for='wiktku-preview-toggle'></label>")
.text(" Pêşdîtina wîkîtekstê");
var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide();
// Live rendered preview (parsed HTML panel, same logic as AjaxEdit).
// Not available on mobile — API polling causes too many problems on small screens.
var livePreviewCheck = null;
var livePreviewPanel = null;
if (!isMobile) {
livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>");
livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide();
}
// Container that holds both panels and switches between stacked / side-by-side.
var previewContainer = $("<div id='wiktku-preview-container'></div>");
previewContainer.append(previewPanel);
if (!isMobile) previewContainer.append(livePreviewPanel);
var previewTimer = null;
var livePreviewIntervalId = -1;
var livePreviewActive = false; // guards in-flight fetches after uncheck
/** Rebuilds wikitext preview if its checkbox is checked. */
function schedulePreview() {
if (!previewCheck.prop("checked")) return;
clearTimeout(previewTimer);
// Debounce: wait 400 ms after the last keystroke before rebuilding.
previewTimer = setTimeout(function () {
getWikitext(function (wikitext) {
previewPanel.text(wikitext).show();
updatePanelLayout();
});
}, 400);
}
/** Fetches parsed HTML and populates the live preview panel. */
function loadLivePreview() {
getWikitext(function (wikitext) {
new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) {
// Discard result if the user unchecked while the fetch was in flight.
if (!livePreviewActive) return;
livePreviewPanel.html(html).show();
updatePanelLayout();
});
});
}
/** Adjusts panel widths depending on which previews are visible. */
function updatePanelLayout() {
var wikitextVisible = previewCheck.prop("checked");
var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked");
if (wikitextVisible && liveVisible) {
// Side by side — each panel takes roughly half the available width.
previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
} else {
// Full width for whichever is visible — only touch panels that are
// actually shown so that setting flex doesn't override display:none.
if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" });
if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" });
}
}
previewCheck.on("change", function () {
if ($(this).prop("checked")) {
schedulePreview();
} else {
clearTimeout(previewTimer);
previewPanel.hide();
updatePanelLayout();
}
});
if (!isMobile) {
livePreviewCheck.on("change", function () {
if ($(this).prop("checked")) {
livePreviewActive = true;
livePreviewPanel.show();
loadLivePreview();
livePreviewIntervalId = setInterval(loadLivePreview, 500);
} else {
livePreviewActive = false;
clearInterval(livePreviewIntervalId);
livePreviewIntervalId = -1;
livePreviewPanel.hide().empty();
updatePanelLayout();
}
});
}
// Listen on all input/change events on the form and update both previews.
startForm.on("input change", function () {
schedulePreview();
});
footer.append($("<span style='margin-left:1em'></span>")
.append(previewCheck).append(previewLabel));
if (!isMobile) {
var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>")
.text(" Pêşdîtina zindî");
footer.append($("<span style='margin-left:1em'></span>")
.append(livePreviewCheck).append(livePreviewLabel));
}
startForm.append(footer);
$("#editform").hide().before(startForm);
// Insert preview container (holds both panels) after the form.
startForm.after(previewContainer);
if (!isMobile) startForm.find("select").chosen(chosenOptions);
refresh();
// Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike.
// Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye
// tên derxistin. Zimanên xardkirî li serî dimînin, yên din
// bi alfabeyê li dû wan tên.
// Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices),
// already loaded into steps.lang.choices before build() runs.
var pinnedChoices = steps.lang.choices.slice();
var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; });
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-translation_editor.js/ziman.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var zimanData;
try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; }
var extraChoices = [];
$.each(zimanData, function (code, name) {
// Filter: skip proto-language codes and legendary/reconstructed langs.
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
// Skip codes already in the pinned list (they are handled above).
if (pinnedCodes.indexOf(code) !== -1) return;
// sortKey: bare name without leading "zimanê " for sorting.
var sortKey = name.replace(/^ziman[eê]\s+/i, "");
var choice = [code, name, sortKey];
extraChoices.push(choice);
langsByCode[code] = choice;
});
// Populate langsByCode for pinned choices too.
$.each(pinnedChoices, function (i, choice) {
// Ensure pinned choices have a sortKey (index 2).
if (choice.length < 3) choice[2] = choice[1];
langsByCode[choice[0]] = choice;
});
// Sort the extra choices alphabetically by sort key.
extraChoices.sort(function (a, b) {
return a[2].localeCompare(b[2], userLang);
});
// Final list: pinned first, then the rest.
var allChoices = pinnedChoices.concat(extraChoices);
steps.lang.choices = allChoices;
steps.trans.keys = allChoices;
steps.dunde.keys = allChoices;
steps.zarava.keys = getZaravaKeys(allChoices);
var langMenu = $("#wiktku-start-lang-field");
populateMenus(langMenu, steps.lang.choices);
langMenu.trigger("chosen:updated");
var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield");
populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans));
transMenus.trigger("chosen:updated");
var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield");
populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde));
dundeMenus.trigger("chosen:updated");
var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield");
populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava));
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page (if the "Li
// cache tomar bike" button was ever used here).
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
});
}
mw.loader.using(["jquery.chosen", "mediawiki.api", "oojs-ui-core"], function () {
if (!isMobile) {
mw.notify(
"Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin",
{ type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 }
);
}
// Load metadata, langChoices, posChoices and tplDefs from the companion
// data page so they can be updated without touching this script.
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-start.js/data.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var d;
try { d = JSON.parse(content); } catch (e) { d = {}; }
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Populate lang choices (pinned list used by build() → ziman.json loader).
if (d.langChoices) steps.lang.choices = d.langChoices;
// Populate POS choices.
if (d.posChoices) steps.pos.choices = d.posChoices;
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the (now populated) lang choices.
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
}).always(build);
});
});
//</nowiki>
k28jh1pbtsqonwlrugw5k2rsccr3pgp
6276939
6276936
2026-07-23T14:52:21Z
Wikihez
7344
6276939
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage)
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var action = mw.config.get("wgAction");
var pageExists = !!mw.config.get("wgArticleId");
var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1";
if (action === "view" && pageExists) {
var targetContainer;
var skin = mw.config.get('skin');
if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Ji vê rûpelê re ziman an jî beşeke nû zêde bike");
return;
}
if (!(action === "edit" || action === "submit")) {
return;
}
if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) {
return;
}
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
// Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra
// whitespace. codePattern: escaped code for one language, or "[^}]+" for any.
function langHeaderSource(codePattern) {
return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Finds a language's header in wikitext (see langHeaderSource). Returns
// { index, match } or null.
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
// Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the
// "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched
// once on first use and cached — every citation row's picker shares it.
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
// Draft cache for this page — lets the editor manually save/restore the
// whole wizard state (shared lang/pron + every section) via
// [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of
// any single browser session. Keyed per page so different articles'
// drafts don't collide.
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
// Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check.
var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches;
var chosenOptions = {
width: "20em",
disable_search_threshold: 0,
placeholder_text_single: "Yekî hilbijêre",
placeholder_text_multiple: "Hilbijêre",
no_results_text: "Nehat dîtin",
enable_split_word_search: true,
search_contains: true };
function wrapNativeSelect(select) {
var placeholder = select.attr("data-placeholder") || "Lê bigere…";
// Hide the native select — we drive it programmatically.
select.hide();
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide();
// Full option cache — rebuilt when chosen:updated fires.
var allOptions = [];
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
if (!v) return; // skip the empty placeholder option
allOptions.push({
value: v,
text: $(this).text(),
disabled: $(this).prop("disabled")
});
});
}
cacheOptions();
function showResults(query) {
var q = query.trim().toLowerCase();
resultsList.empty();
var matched;
if (q) {
matched = [];
$.each(allOptions, function (_, o) {
if (o.disabled) return;
var codeMatch = o.value.toLowerCase().indexOf(q) !== -1;
var nameMatch = o.text.toLowerCase().indexOf(q) !== -1;
if (codeMatch || nameMatch) {
matched.push({ opt: o, codeMatch: codeMatch });
}
});
// Code matches (especially prefix matches) come first.
matched.sort(function (a, b) {
var aCode = a.opt.value.toLowerCase();
var bCode = b.opt.value.toLowerCase();
var aPrefix = aCode.indexOf(q) === 0;
var bPrefix = bCode.indexOf(q) === 0;
if (aPrefix !== bPrefix) return aPrefix ? -1 : 1;
if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1;
return 0;
});
matched = matched.map(function (item) { return item.opt; });
} else {
matched = allOptions.filter(function (o) { return !o.disabled; });
}
if (!matched.length) {
resultsList.hide();
return;
}
$.each(matched.slice(0, 50), function (_, opt) {
var li = $("<li></li>").text(opt.text).attr("data-value", opt.value);
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// Keep mousedown for desktop fallback.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
resultsList.append(li);
});
// Position the list above or below depending on available viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px ≈ 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
resultsList.show();
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
resultsList.hide();
select.trigger("change");
}
textInput.on("input focus", function () {
showResults($(this).val());
});
textInput.on("blur", function () {
// Short delay so a touchstart on a result item fires first.
setTimeout(function () { resultsList.hide(); }, 200);
});
// chosen:updated → re-cache (called after ziman.json loads).
select.on("chosen:updated", function () {
cacheOptions();
// If a value is already selected, keep the display text in sync.
var cur = select.val();
if (cur) {
var match = $.grep(allOptions, function (o) { return o.value === cur; })[0];
if (match) textInput.val(match.text);
}
});
// No-op .chosen() so desktop call-sites don't need guarding.
select.chosen = function () { return select; };
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
var langsByCode = {};
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
// Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin —
// ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin.
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c[0]] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a
// template-based sense has no "own" gloss for alternate forms, synonyms,
// antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket,
// Nimûne and Jêgirtin stay visible either way.
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{bênimûne|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = text.replace(
new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"),
"'''$1'''");
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + n.tr;
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + n.t;
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 200 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote;
if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr;
if (jeder.quoteT) out += "|tercime=" + jeder.quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
function makeWordListStep(id, name, question_fmt, header, templateName) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
if (words.length === 1) {
// Single word — keep the simple inline template.
return section +
"* {{" + templateName + "|" + lang + "|" +
words[0] + "}}\n";
}
// Multiple words — use {{kol3}} with one positional param per word.
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
} },
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: [],
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (_, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(tpl.name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
}
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line.charAt(0) === "*" ? line : "* " + line;
}).join("\n");
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var cinsiyet = values.cinsiyet || ""; // from the cinsiyet step
var lekTur = values.lektur || ""; // from the lêker transitivity step
var capitalizedPos = capitalize(pos);
var outputString = "\n=== " + capitalizedPos + " ===\n";
// Add ku-tewîn-nav for single-word navdêr/serenav with known gender.
if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] &&
(pos === "navdêr" || pos === "serenav") && values.lang === "ku") {
outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet];
if (pos === "serenav") outputString += "|y";
outputString += "}}\n";
}
outputString += "{{" + pos + "|" + values.lang;
if (pos === "navdêr" || pos === "serenav") {
if (cinsiyet) {
outputString += "|c=" + cinsiyet;
} else if (values.lang === "ku") {
// Unknown gender — prompt editor to fill it in.
outputString += "|c=?";
}
} else if (/^lêker/.test(pos) && lekTur) {
outputString += "|c=" + lekTur;
}
if (values.tr) {
outputString += "|tr=" + values.tr;
}
outputString += "}}\n";
return outputString;
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function (values) {
var meta = metadata[values.lang];
return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") &&
meta && meta.g && meta.g.length);
},
build: function (stepDiv, values) {
var select = stepDiv.find("select.wiktku-start-step-field");
if (!select.length) {
select = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-cinsiyet-field" }));
stepDiv.append(select);
}
if (!values) return select;
var meta = metadata[values.lang];
var codes = (meta && meta.g) || [];
var prevVal = select.val();
var choices = $.map(codes, function (code) {
return [[code, genderLabel(code)]];
});
populateMenus(select, choices);
if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) {
select.val(prevVal);
}
if (!isMobile) select.trigger("chosen:updated");
return select;
},
output: function () { return ""; } // consumed by pos.output
},
// Lêker transitivity — shown for any lêker variant.
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function (values) {
return values.lang && values.pos && /^lêker/.test(values.pos);
},
// Dictionary: value → display label. Add new types here.
choices: [
["gh", "gerguhêz"],
['x-gh', 'xwerû-gerguhêz'],
['p-gh', 'pêkhatî-gerguhêz'],
['h-gh', 'hevedudanî-gerguhêz'],
['b-gh', 'biwêjî-gerguhêz'],
["ngh", "negerguhêz"],
['x-ngh', 'xwerû-negerguhêz'],
['p-ngh', 'pêkhatî-negerguhêz'],
['h-ngh', 'hevedudanî-negerguhêz'],
['b-ngh', 'biwêjî-negerguhêz'] ],
output: function () { return ""; } // consumed by pos.output
},
tr: {
id: "tr",
name: "transkrîpsiyon",
question_fmt: "Transkrîpsiyona kelîmeyê",
multiple: false,
narrow: true,
visible: function (values) {
return values.lang && values.pos && needsTransliteration(values.lang);
} },
dfn: {
id: "dfn",
// required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne
name: "Mane",
question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!",
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function (stepDiv) {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
dfnArea.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || [];
// Cache of all non-placeholder option elements — used by updateTplOptions
// to remove/re-add options (the only approach compatible with Chosen.js).
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (_, tpl) {
var opt = $("<option></option>")
.val(tpl.name)
.text(tpl.name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible ones.
// This is the only approach that works correctly with both native <select>
// and Chosen.js on desktop.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
if (!isMobile) tplSelect.trigger("chosen:updated");
if (currentVal && !currentStillValid) {
tplSelect.val("");
dfnArea.show();
tplTitleInput.hide().val("");
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpelê…")
.hide();
// Grow the sense list when the title field of the last block gets content,
// mirroring what onDfnInput does for the plain mane textarea.
tplTitleInput.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after programmatically setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
tplTitleInput.show().focus();
} else {
dfnArea.show();
tplTitleInput.hide().val("");
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput);
block.append(tplRow);
block.append(dfnArea);
// Apply lang filter on initial build.
updateTplOptions();
// — Sub-field groups —
// All groups are always visible with a placeholder textarea.
// Nimûne is last in the DOM and last in the output.
var allGroups = [
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in" },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" },
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": ");
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": " + g.izah);
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", g.label + "…");
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow);
});
list.append($("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder (citation) group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text("Jêgirtin: ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
var dragHandle = $("<span></span>")
.addClass("wiktku-jeder-drag-handle")
.attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(dragHandle).append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<input type='text'>")
.addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow")
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi Citoid bîne",
classes: [ "wiktku-dfn-quote-citoid-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoidHelper.ensure().then(function () {
return wikt.citoid.fetchJederFields(query);
}).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
srcSel.trigger("chosen:updated");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
if (!chosen) return;
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
// so the saved value is always exactly what's in that
// (editable) textarea — no separate hidden field that can
// end up empty and silently fall back to "çavkanî?".
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Navê şablonê lê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
function showCatResults(query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
}
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
function selectCatTpl(name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
}
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (_, f) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(f.label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", f.key)
.attr("placeholder", f.label + "…");
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
// Make citation rows sortable (drag to reorder).
// The last row is always the empty trailing row — cancel its drag.
mw.loader.using("jquery.ui.sortable", function () {
jederList.sortable({
handle: ".wiktku-jeder-drag-handle",
axis: "y",
items: "li:not(:last-child)",
tolerance: "pointer"
});
});
jederGroupDiv.append(jederLbl).append(jederList);
block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
return block;
},
/** Grows the sense list when the last mane textarea gets content. */
onDfnInput: function (stepDiv, block, textarea) {
var val = $.trim($(textarea).val());
var list = stepDiv.find("ol.wiktku-dfn-list");
var isLast = block.is(list.children("li:last-child"));
if (val && isLast) {
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
// Apply Chosen to the new block's template selector on desktop.
if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions);
}
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow);
});
list.append($("<li></li>").append(next));
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: [],
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
// True only when at least one sense has a regular mane (needs linkify).
var hasRegularMane = senses.some(function (s) {
return !s.tpl && s.mane !== "";
});
// nimûne/jêder must never pass through linkify, only the mane text.
// defLines (linkified) and citeLines (kept aside) stay parallel;
// a {{wiktku-cite-N}} token in defLines marks where to splice back.
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
$.each(senses, function (_, sense) {
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
var idx = defLines.length;
var cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
if (dfn) {
dfn = stripWikilinks(dfn);
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
} else {
dfn = "{{mane?|" + lang + "}}";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
var cite = buildCite(sense, lang);
var idx = defLines.length;
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with bênimûne.
text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n";
callback(text);
return;
}
if (!hasRegularMane) {
// All filled senses are template senses — no linkify needed.
callback(restoreCites(text));
return;
}
var didTryToLoad = function () {
if (typeof $.wiktviLinkify === "function") {
$.wiktviLinkify(text, function (linked) {
callback(restoreCites(linked));
});
} else {
callback(restoreCites(text));
}
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"),
binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: [],
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls array.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (_, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
ta.focus();
});
wrap.append(sel).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder,
// Only when a regular sense has content (real mane, or {{mane?}}
// with other fields filled in) — a şablon sense never counts.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (!hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: {
"navz": true},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {
"navz": true
},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
if (!wordLines) return undefined;
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — chosen is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Zimanekî lê bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
if (isMobile) {
row.append(wrapNativeSelect(langSel)).append(wordList);
} else {
row.append(langSel).append(wordList);
}
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Init chosen after the new row is in the DOM (desktop only).
if (!isMobile) newRow.find("select").chosen(chosenOptions);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("chosen:updated");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Init chosen after the select is in the DOM (desktop only).
if (!isMobile) list.find("select").chosen(chosenOptions);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it — nothing to output, and gloss.text is
// empty so there's no caption to give {{werger-ser}} anyway.
if (!gloss.text && !rows.length) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din ên (ne jêgirtinan) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
// Notify Chosen that the option list has changed (no-op on mobile).
if (!isMobile) sel.trigger("chosen:updated");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji jêrê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
if (!isMobile) sel.trigger("chosen:updated");
ta.focus();
});
wrap.append(sel).append(status).append($("<br>")).append(ta);
stepDiv.append(wrap);
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
var ordered_steps = [
steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr,
steps.dfn, steps.je, steps.tekildar,
steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani];
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "cinsiyet", "lektur", "tr",
"dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing list so build() recreates it cleanly.
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set programmatically (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true);
});
subList.append($("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text);
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote);
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Notify chosen widgets of the programmatic value change.
if (!isMobile) fields.filter("select").trigger("chosen:updated");
});
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang/pron header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var entry = steps.lang.output(sharedValues);
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) { return s.mane; }).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>"));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice[1]);
if (choice[2] !== undefined) {
// This is a language choice [code, name, sortKey] — show code.
displayText = capitalize(choice[1]) + " (" + choice[0] + ")";
}
var option = $(mw.html.element("option", {
value: choice[0] }, displayText));
if (disabledValues[choice[0]]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
if (isMobile) {
populateMenus(input, step.keys, getDisabledKeys(step));
// On mobile return the wrapper; the select is inside it.
// Change events still fire on the inner select.
return wrapNativeSelect(input);
}
input.on("chosen:showing_dropdown", function () {
$(this).data("wiktku-defaultValue", $(this).val());
});
input.change(function (_, context) {
var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this);
var oldValue = $(this).data("wiktku-defaultValue");
var newValue = context.selected;
inputs.find("option[value='" + oldValue + "']").prop('disabled', false);
inputs.find("option[value='" + newValue + "']").prop('disabled', true);
inputs.trigger("chosen:updated");
});
populateMenus(input, step.keys, getDisabledKeys(step));
return input;
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) nextKeyInput.chosen(chosenOptions);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var didFocus = false;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transkrîpsiyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transkrîpsiyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
li.find(".wiktku-dfn-nimune-quote").after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê ya kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
if (typeof(step.visible) === "function" && !step.visible(values)) {
$("#wiktku-start-" + stepId).slideUp();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
var didSlideDown;
if (!didFocus && stepDiv.css("display") === "none" && step.required) {
didSlideDown = function (evt) {
input.focus();
};
didFocus = true;
}
stepDiv.slideDown(didSlideDown);
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
continueBtn.setDisabled(!canContinue);
}
function destroy() {
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard, #editform").slideDown();
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and the navz pseudo-code.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections at all — just append.
var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : "";
return existing + sep + wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry[1] : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections separated by newlines.
var joined = sections.map(function (s) {
// Ensure each section ends with exactly one newline before the next.
return s.content.replace(/\n+$/, "");
}).join("\n");
return preamble + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê beşeke nû li rûpelê îlawe bikî. Eger ev zimanê ku tu îlawe dikî jixwe di rûpelê de hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bikin, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Naveroka rûpelê paqij bike (rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
spinner.show();
getWikitext(function (wikitext) {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page and start fresh — just
// write the newly generated wikitext as the full content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
$("#wiktku-preview-container").remove();
destroy();
});
});
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
stepDiv.append($(mw.html.element("h3", {
"class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", {
"for": "wiktku-start-" + step.id + "-field" }, name)))));
if (step.question_fmt) {
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, step.question_fmt)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
if (isMobile) {
stepDiv.append(wrapNativeSelect(input));
} else {
stepDiv.append(input);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append($("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Insert tab bar just before the first per-section step.
if (step.id === "pos") startForm.append(sectionTabsDiv);
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beş zêde bike",
disabled: true
});
addSectionBtn.on("click", function () {
saveCurrentSection();
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
var stepDiv = $("#wiktku-start-dfn");
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
var stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated");
});
renderSectionTabs();
refresh();
$("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300);
});
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
return;
}
sectionTabsDiv.show();
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection) return;
saveCurrentSection();
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
}
renderSectionTabs();
/**
* Restores a full cached draft: shared lang/pron fields, then every
* section (via the same restoreSection()/refresh()/renderSectionTabs()
* sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
mw.notify("Danûstandina berê ya tomarkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
renderSectionTabs();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
mw.notify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
// which is invisible on mobile and easy to miss on desktop.
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
framed: false
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load (see the
// ziman.json .then() callback below), once the full language list
// is available.
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache tomar bike",
title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
var snapshot = {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
sections: sections,
activeSection: activeSection
};
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
formCache.Set(snapshot);
mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" });
}
});
footer.append(saveCacheBtn.$element);
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
footer.append(clearCacheBtn.$element);
// ── Preview checkboxes ───────────────────────────────────────────────
// Wikitext preview (raw text panel)
var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>");
var previewLabel = $("<label for='wiktku-preview-toggle'></label>")
.text(" Pêşdîtina wîkîtekstê");
var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide();
// Live rendered preview (parsed HTML panel, same logic as AjaxEdit).
// Not available on mobile — API polling causes too many problems on small screens.
var livePreviewCheck = null;
var livePreviewPanel = null;
if (!isMobile) {
livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>");
livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide();
}
// Container that holds both panels and switches between stacked / side-by-side.
var previewContainer = $("<div id='wiktku-preview-container'></div>");
previewContainer.append(previewPanel);
if (!isMobile) previewContainer.append(livePreviewPanel);
var previewTimer = null;
var livePreviewIntervalId = -1;
var livePreviewActive = false; // guards in-flight fetches after uncheck
/** Rebuilds wikitext preview if its checkbox is checked. */
function schedulePreview() {
if (!previewCheck.prop("checked")) return;
clearTimeout(previewTimer);
// Debounce: wait 400 ms after the last keystroke before rebuilding.
previewTimer = setTimeout(function () {
getWikitext(function (wikitext) {
previewPanel.text(wikitext).show();
updatePanelLayout();
});
}, 400);
}
/** Fetches parsed HTML and populates the live preview panel. */
function loadLivePreview() {
getWikitext(function (wikitext) {
new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) {
// Discard result if the user unchecked while the fetch was in flight.
if (!livePreviewActive) return;
livePreviewPanel.html(html).show();
updatePanelLayout();
});
});
}
/** Adjusts panel widths depending on which previews are visible. */
function updatePanelLayout() {
var wikitextVisible = previewCheck.prop("checked");
var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked");
if (wikitextVisible && liveVisible) {
// Side by side — each panel takes roughly half the available width.
previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" });
} else {
// Full width for whichever is visible — only touch panels that are
// actually shown so that setting flex doesn't override display:none.
if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" });
if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" });
}
}
previewCheck.on("change", function () {
if ($(this).prop("checked")) {
schedulePreview();
} else {
clearTimeout(previewTimer);
previewPanel.hide();
updatePanelLayout();
}
});
if (!isMobile) {
livePreviewCheck.on("change", function () {
if ($(this).prop("checked")) {
livePreviewActive = true;
livePreviewPanel.show();
loadLivePreview();
livePreviewIntervalId = setInterval(loadLivePreview, 500);
} else {
livePreviewActive = false;
clearInterval(livePreviewIntervalId);
livePreviewIntervalId = -1;
livePreviewPanel.hide().empty();
updatePanelLayout();
}
});
}
// Listen on all input/change events on the form and update both previews.
startForm.on("input change", function () {
schedulePreview();
});
footer.append($("<span style='margin-left:1em'></span>")
.append(previewCheck).append(previewLabel));
if (!isMobile) {
var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>")
.text(" Pêşdîtina zindî");
footer.append($("<span style='margin-left:1em'></span>")
.append(livePreviewCheck).append(livePreviewLabel));
}
startForm.append(footer);
$("#editform").hide().before(startForm);
// Insert preview container (holds both panels) after the form.
startForm.after(previewContainer);
if (!isMobile) startForm.find("select").chosen(chosenOptions);
refresh();
// Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike.
// Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye
// tên derxistin. Zimanên xardkirî li serî dimînin, yên din
// bi alfabeyê li dû wan tên.
// Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices),
// already loaded into steps.lang.choices before build() runs.
var pinnedChoices = steps.lang.choices.slice();
var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; });
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-translation_editor.js/ziman.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var zimanData;
try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; }
var extraChoices = [];
$.each(zimanData, function (code, name) {
// Filter: skip proto-language codes and legendary/reconstructed langs.
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
// Skip codes already in the pinned list (they are handled above).
if (pinnedCodes.indexOf(code) !== -1) return;
// sortKey: bare name without leading "zimanê " for sorting.
var sortKey = name.replace(/^ziman[eê]\s+/i, "");
var choice = [code, name, sortKey];
extraChoices.push(choice);
langsByCode[code] = choice;
});
// Populate langsByCode for pinned choices too.
$.each(pinnedChoices, function (i, choice) {
// Ensure pinned choices have a sortKey (index 2).
if (choice.length < 3) choice[2] = choice[1];
langsByCode[choice[0]] = choice;
});
// Sort the extra choices alphabetically by sort key.
extraChoices.sort(function (a, b) {
return a[2].localeCompare(b[2], userLang);
});
// Final list: pinned first, then the rest.
var allChoices = pinnedChoices.concat(extraChoices);
steps.lang.choices = allChoices;
steps.trans.keys = allChoices;
steps.dunde.keys = allChoices;
steps.zarava.keys = getZaravaKeys(allChoices);
var langMenu = $("#wiktku-start-lang-field");
populateMenus(langMenu, steps.lang.choices);
langMenu.trigger("chosen:updated");
var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield");
populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans));
transMenus.trigger("chosen:updated");
var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield");
populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde));
dundeMenus.trigger("chosen:updated");
var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield");
populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava));
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page (if the "Li
// cache tomar bike" button was ever used here).
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
});
}
mw.loader.using(["jquery.chosen", "mediawiki.api", "oojs-ui-core"], function () {
if (!isMobile) {
mw.notify(
"Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin",
{ type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 }
);
}
// Load metadata, langChoices, posChoices and tplDefs from the companion
// data page so they can be updated without touching this script.
new mw.Api().get({
action: "query",
prop: "revisions",
titles: "MediaWiki:Gadget-start.js/data.json",
rvprop: "content",
rvslots: "main",
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var content = page &&
page.revisions &&
page.revisions[0].slots.main.content;
var d;
try { d = JSON.parse(content); } catch (e) { d = {}; }
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Populate lang choices (pinned list used by build() → ziman.json loader).
if (d.langChoices) steps.lang.choices = d.langChoices;
// Populate POS choices.
if (d.posChoices) steps.pos.choices = d.posChoices;
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the (now populated) lang choices.
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
}).always(build);
});
});
//</nowiki>
ir0kazpxyk0jvwshuoy3pygvsux5fjm
Modul:usex/ceribandin
828
1325948
6276983
6105851
2026-07-23T22:18:49Z
Wikihez
7344
6276983
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if source then
ins(" " .. source)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or source or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if source or footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(source)
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
d2r6woihv82lk9j1ssoqevvat94azku
6276984
6276983
2026-07-23T22:26:55Z
Wikihez
7344
6276984
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if source then
ins(" " .. source)
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or source or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if source or footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(source)
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
9gjzm3b8fv8pn3ofb8m21rusgz3l3ne
6276985
6276984
2026-07-23T22:27:45Z
Wikihez
7344
Guhartoya [[Special:Diff/6276984|6276984]] yê [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|gotûbêj]]) şûnde kir
6276985
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if source then
ins(" " .. source)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or source or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if source or footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(source)
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
d2r6woihv82lk9j1ssoqevvat94azku
6276989
6276985
2026-07-23T22:50:46Z
Wikihez
7344
6276989
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
-- source now appears right after the usex text (+ audio), before tr/ts/norm/translation/lit/footer
if source then
ins(" " .. source)
end
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
if source or any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
-- source now appears right after the usex text, as the first <dd> line
insert_dd(source)
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
local any_orig_annotations = orig_obj.tr or orig_obj.ts or orig_obj.norm
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
insert_dd(footer)
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
obwnfg3d6wi576r2zn6jor7y8s9k7w7
6276990
6276989
2026-07-23T22:53:16Z
Wikihez
7344
Restored revision 6276985 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6276990
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if source then
ins(" " .. source)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or source or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if source or footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(source)
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
d2r6woihv82lk9j1ssoqevvat94azku
6276991
6276990
2026-07-23T22:57:30Z
Wikihez
7344
6276991
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if source then
ins(" " .. source)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
-- Show the source directly after the quotation text (like the inline
-- form "test ― (2023, +)"), not down in the <dl> below the translation.
if source then
ins(" ― " .. source)
end
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
local result
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
qi4ylw5i585h2gph2mvlyq5md4ziqa9
6276992
6276991
2026-07-23T22:58:17Z
Wikihez
7344
6276992
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if source then
ins(" " .. source)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
-- Show the source directly after the quotation text (like the inline
-- form "test ― (2023, +)"), not down in the <dl> below the translation.
if source then
ins(" ― " .. source)
end
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
3d8zqom7vkkhe45d7zkrvzldoz0b1nh
6276993
6276992
2026-07-23T23:05:15Z
Wikihez
7344
6276993
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'e-source',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = "(" .. span(css_classes.source, source) .. ")"
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
-- source now appears right after the usex text (+ audio), before tr/ts/norm/translation/lit/footer
if source then
ins(" " .. source)
end
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(format_audio(audio))
-- source stays on the same line as the usex text, since <dd> is block-level
-- and would otherwise force it onto its own line.
if source then
ins(" " .. source)
end
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
local any_orig_annotations = orig_obj.tr or orig_obj.ts or orig_obj.norm
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
insert_dd(footer)
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
m6f9c406376485y0zfg8f0q5kvnfmav
6276994
6276993
2026-07-23T23:14:07Z
Wikihez
7344
Restored revision 6105851 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6276994
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'jeder',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang, style)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if style then
style = ' style="' .. style .. '"'
else
style = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, style, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = wrap("span", "jeder", " — (" .. source .. ")", nil, "font-size: 0.85em")
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
if source then
ins(" " .. source)
end
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(source)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
51knenvqcymef8ceu7b1ozkhbt07o3e
6276998
6276994
2026-07-23T23:43:59Z
Wikihez
7344
6276998
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'jegirtin-text',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'jeder',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang, style)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if style then
style = ' style="' .. style .. '"'
else
style = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, style, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = wrap("span", "jeder", " — (" .. source .. ")", nil, "font-size: 0.85em")
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
if source then
ins(" " .. source)
end
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(source)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
o8vzdqc1aoaqzzy2nl63nt7w3cn8wkt
6277001
6276998
2026-07-23T23:50:38Z
Wikihez
7344
6277001
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'jegirtin-text',
example = 'e-example',
quotation = 'e-quotation',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'jeder',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang, style)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if style then
style = ' style="' .. style .. '"'
else
style = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, style, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = wrap("span", "jeder", " — (" .. source .. ")", nil, "font-size: 0.85em; font-style: normal")
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
if source then
ins(" " .. source)
end
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(source)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
jhcunwnf30txvu1smbu4dxxg7eizrak
6277010
6277001
2026-07-24T00:29:27Z
Wikihez
7344
6277010
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'jegirtin-text',
quotation_with_citation = 'e-quotation cited-passage',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'jeder',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang, style)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if style then
style = ' style="' .. style .. '"'
else
style = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, style, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = wrap("span", "jeder", " — (" .. source .. ")", nil, "font-size: 0.85em; font-style: normal")
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
if source then
ins(" " .. source)
end
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(source)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
pxj46zj00hqgwa208g7dw8rkbn5if4f
6277011
6277010
2026-07-24T00:30:58Z
Wikihez
7344
6277011
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'jegirtin-text',
quotation_with_citation = 'jegirtin-text',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'jeder',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang, style)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if style then
style = ' style="' .. style .. '"'
else
style = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, style, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = wrap("span", "jeder", " — (" .. source .. ")", nil, "font-size: 0.85em; font-style: normal")
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
if source then
ins(" " .. source)
end
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(source)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
cbq6kru63djx9g9q5kdz9gmp6nc0fvn
6277013
6277011
2026-07-24T00:32:45Z
Wikihez
7344
6277013
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local links_module = "Module:links"
local scripts_module = "Module:scripts"
local script_utilities_module = "Module:script utilities"
local string_utilities_module = "Module:string utilities"
local usex_data_module = "Module:usex/data"
local m_str_utils = require(string_utilities_module)
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local rfind = m_str_utils.find
local uupper = m_str_utils.upper
local ulen = m_str_utils.len
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local translit_data = mw.loadData("Module:transliteration/data")
local needs_translit = translit_data[1]
local BRACKET_SUB = u(0xFFF0)
local original_text = "<small>''ya orîjînal:''</small> "
-- 100 more or less corresponds to the setting of 30 for the example text alone as formerly used in
-- {{hi-x}} and {{ur-x}}, taking into account transliteration, gloss and formatting characters.
-- FIXME: We should have different widths for desktop vs. mobile and generate the appropriate CSS so
-- both are handled correctly.
local MAX_INLINE_WIDTH = 100 -- In characters. HACK! FIXME! Do this a better way.
-- List of scripts whose characters are double-width/full-width.
local double_width_scripts = {"Hani", "Hrkt", "Hang"}
-- microformat2 classes, see https://phabricator.wikimedia.org/T138709
local css_classes = {
container_ux = 'h-usage-example',
container_quotation = 'h-quotation',
example = 'e-example',
quotation = 'jegirtin-text',
quotation_with_citation = 'jegirtin-text',
translation = 'e-translation',
-- The following are added by [[Module:script utilities]], using [[Module:script utilities/data]]
-- transliteration = 'e-transliteration',
-- transcription = 'e-transcription',
normalization = 'e-normalization',
literally = 'e-literally',
qualifier = 'e-qualifier',
source = 'jeder',
footer = 'e-footer'
}
-- helper functions
local function track(page)
require(debug_track_module)("usex/" .. page)
return true
end
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function wrap(tag, class, text, lang, style)
if lang then
lang = ' lang="' .. lang .. '"'
else
lang = ""
end
if style then
style = ' style="' .. style .. '"'
else
style = ""
end
if text and class then
return table.concat{'<', tag, ' class="', class, '"', lang, style, '>', text, '</', tag, '>'}
else
return nil
end
end
local function span(class, text) return wrap('span', class, text) end
local function div(class, text) return wrap('div', class, text) end
-- Remove any HTML from the formatted text and resolve links, since the extra characters don't contribute to the
-- displayed length.
local function convert_to_raw_text(text)
text = rsub(text, "<.->", "")
if text:find("%[%[") then
text = require(links_module).remove_links(text)
end
return text
end
local function get_character_width(text)
local charsets = {}
for _, script in ipairs(double_width_scripts) do
table.insert(charsets, require(scripts_module).getByCode(script):getCharacters())
end
local single_width_chars = ulen(rsub(text, "[" .. table.concat(charsets) .. "]", ""))
local total_chars = ulen(text)
local double_width_chars = total_chars - single_width_chars
return single_width_chars + 2 * double_width_chars
end
--[==[
Apply the substitutions in `subst` (from the {{para|subst}} parameter or similar) to the example or quotation in
`usex` after removing links, returning the resulting text. `track`, if supplied, is a function of one argument that is
used to insert tracking categories: one for any call to this function, another if a single / is used in the `subst`
argument.
]==]
function export.apply_subst(usex, subst, track)
local subbed_usex = require(links_module).remove_links(usex)
local function do_track(page)
if track then
track(page)
end
return true
end
if subst then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst]]
do_track("subst")
subst = rsplit(subst, ",")
for _, subpair in ipairs(subst) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/usex/subst-single-slash]]
local subsplit = rsplit(subpair, rfind(subpair, "//") and "//" or do_track("subst-single-slash") and "/")
subbed_usex = rsub(subbed_usex, subsplit[1], subsplit[2])
end
end
return subbed_usex
end
--[=[
Process parameters for usex text (either the primary text or the original text) and associated annotations. On input,
the following fields are recognized in `data` (all are optional except as marked):
* `lang`: Language object of text; may be an etymology language (REQUIRED).
* `termlang`: The language object of the term being illustrated, which may be different from the language of the main
quotation text and should always be based off of the main text, not the original text. Used for
categories. May be an etymology language (REQUIRED).
* `usex`: Text of usex/quotation.
* `sc`: Script object of text.
* `tr`: Manual transliteration.
* `ts`: Transcription.
* `norm`: Normalized version of text.
* `normsc`: Script object of normalized version of text, or "auto".
* `subst`: String of substitutions for transliteration purposes.
* `quote`: If non-nil, this is a quotation (using {{tl|quote}} or {{tl|quote-*}}) instead of a usage example (using
{{tl|usex}}). If it has the specific value "quote-meta", this is a quotation with citation (invoked from
{{tl|quote-*}}). This controls the CSS class used to display the quotation, as well as the face used to tag the usex
(which in turn results in the usex being upright text if a quotation, and italic text if a usage example).
* `title`: Title object of the current page (REQUIRED).
* `q`: List of left qualifiers.
* `qq`: List of right qualifiers.
* `ref`: String to display directly after any right qualifier, with no space. (FIXME: Should be converted into
an actual ref.)
* `nocat`: Overall `data.nocat` value.
* `categories`: List to insert categories into (REQUIRED).
* `example_type`: Either "quotation" (if `quote` specified) or "usage example" (otherwise) (REQUIRED).
On output, return an object with four fields:
* `usex`: Formatted usex, including qualifiers attached to both sides and `ref` attached to the right. Always specified.
* `tr`: Formatted transliteration; may be nil.
* `ts`: Formatted transcription; may be nil.
* `norm`: Formatted normalized version of usex; may be nil.
]=]
local function process_usex_text(data)
local lang = data.lang
local termlang = data.termlang
local usex = data.usex
local sc = data.sc
local sc_explicit = sc
local tr = data.tr
local ts = data.ts
local norm = data.norm
local normsc = data.normsc
local subst = data.subst
local quote = data.quote
local leftq = data.q
local rightq = data.qq
local ref = data.ref
local nocat = data.nocat
local categories = data.categories
local example_type = data.example_type
local title = data.title
if normsc == "auto" then
normsc = nil
elseif not normsc then
normsc = sc
end
if not sc then
sc = lang:findBestScript(usex)
end
if not normsc and norm then
normsc = lang:findBestScript(norm)
end
local langcode = lang:getFullCode()
-- tr=- means omit transliteration altogether
if tr == "-" then
tr = nil
else
-- Try to auto-transliterate.
if not tr then
-- First, try transliterating the normalization, if supplied.
if norm and normsc and not normsc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
local subbed_norm = export.apply_subst(norm, subst, track)
tr = (lang:transliterate(subbed_norm, normsc))
end
-- If no normalization, or the normalization is in a Latin script, or the transliteration of the
-- normalization failed, fall back to transliterating the usex.
if not tr then
local subbed_usex = export.apply_subst(usex, subst, track)
tr = (lang:transliterate(subbed_usex, sc))
end
-- If the language doesn't have capitalization and is specified in [[Module:usex/data]], then capitalize any sentences.
-- Exclamation marks and question marks need to be unescaped then re-escaped.
if tr and mw.loadData(usex_data_module).capitalize_sentences[langcode] then
tr = tr:gsub("!", "!")
:gsub("?", "?")
tr = rsub(tr, "%f[^%z%p%s](.)(.-[%.%?!‽])", function(m1, m2)
return uupper(m1) .. m2
end)
tr = tr:gsub("!", "!")
:gsub("%?", "?")
end
end
-- If there is still no transliteration, then add a cleanup category.
if not tr and needs_translit[langcode] and not sc:getCode():find("Lat") and sc:getCode() ~= "None" then
table.insert(categories, ("Daxwazên translîterasyona %syan bi %s"):format(example_type,
lang:getCanonicalName()))
end
end
if tr then
tr = require(script_utilities_module).tag_translit(tr, langcode, "usex")
end
if ts then
ts = require(script_utilities_module).tag_transcription(ts, langcode, "usex")
ts = "/" .. ts .. "/"
end
local function do_language_and_script_tagging(usex, lang, sc, css_class)
usex = require(links_module).embedded_language_links{term = usex, lang = lang, sc = sc}
local face
if quote then
face = nil
else
face = "term"
end
usex = require(script_utilities_module).tag_text(usex, lang, sc, face, css_class)
return usex
end
if usex then
usex = do_language_and_script_tagging(usex, lang, sc,
quote == "quote-meta" and css_classes.quotation_with_citation or
quote and css_classes.quotation or css_classes.example)
if not nocat then
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
-- Here we don't want to use the subpage text because we check [[Citations:foo]] against [[foo]] and
-- if there's a slash in what follows 'Citations:', we want to check against the full page with the
-- slash.
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
-- Categories beginning with the language name should use full languages as that's what the poscat
-- system requires, but 'Requests for' categories can use etymology-only languages.
table.insert(categories, ("%s bi %s"):format(ucfirst(example_type), termlang:getFullName()))
end
end
else
if tr then
table.insert(categories, ("Daxwazên skrîpta nimûneyan bi %s"):format(lang:getCanonicalName()))
end
-- TODO: Trigger some kind of error here
usex = "<small>(ji kerema xwe metna orîjînal a vê nimûneyê binivîsin)</small>"
end
if norm then
-- Use brackets in HTML entity format just to make sure we don't interfere with links; add brackets before
-- script tagging so that if the script tagging increases the font size, the brackets get increased too.
norm = "[" .. norm .. "]"
norm = do_language_and_script_tagging(norm, lang, normsc, css_classes.normalization)
end
local result = {}
if leftq and #leftq > 0 then
table.insert(result, span(css_classes.qualifier, require("Module:qualifier").format_qualifier(leftq)) .. " ")
end
table.insert(result, usex)
if rightq and #rightq > 0 then
table.insert(result, " " .. span(css_classes.qualifier, require("Module:qualifier").format_qualifier(rightq)))
end
if ref and ref ~= "" then
track("ref")
table.insert(result, ref)
end
return {
usex = table.concat(result),
tr = tr,
ts = ts,
norm = norm
}
end
local function format_audio(audio)
if audio then
return " [[File:" .. audio .. "|25px]]"
else
return ""
end
end
--[==[
Format a usex or quotation. Implementation of {{tl|ux}}, {{tl|quote}} and {{tl|quote-*}} templates (e.g.
{{tl|quote-book}}, {{tl|quote-journal}}, {{tl|quote-web}}, etc.). FIXME: Should also be used by {{tl|Q}} and
[[Module:Quotations]].
Takes a single object `data`, containining the following fields:
* `usex`: The text of the usex or quotation to format. Semi-mandatory (a maintenance line is displayed if missing).
* `lang`: The language object of the text. Mandatory. May be an etymology language.
* `termlang`: The language object of the term, which may be different from the language of the text. Defaults to `lang`.
Used for categories. May be an etymology language.
* `sc`: The script object of the text. Autodetected if not given.
* `quote`: If specified, this is a quotation rather than a usex (uses a different CSS class that affects formatting).
* `inline`: If specified, format the usex or quotation inline (on one line).
* `translation`: Translation of the usex or quotation, if in a foreign language.
* `lit`: Literal translation (if the translation in `translation` is idiomatic and differs significantly from the
literal translation).
* `normalization`: Normalized version of the usex or quotation (esp. for older languages where nonstandard spellings
were common).
* `normsc`: Script object of the normalized text. If unspecified, use the script object given in `sc` if any, otherwise
do script detection on the normalized text. If "auto", do script detection on the normalized text even if
a script was specified in `sc`.
* `transliteration`: Transliteration of the usex. If unspecified, transliterate the normalization if specified and not
in a Latin script and transliterable, otherwise fall back to transliterating the usex text.
* `transcription`: Transcription of the usex, for languages where the transliteration differs significantly from the
pronunciation.
* `subst`: String indicating substitutions to perform on the usex/quotation and normalization prior to transliterating
them. Multiple substs are comma-separated and individual substs are of the form FROM//TO where FROM is a
Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is present in the
substitution.)
* `q`: If specified, a list of left qualifiers to display before the usex/quotation text.
* `qq`: If specified, a list of right qualifiers to display after the usex/quotation text.
* `qualifiers`: If specified, a list of right qualifiers to display after the usex/quotation text, for compatibility
purposes.
* `ref`: Reference text to display directly after the right qualifiers. (FIXME: Instead, this should be actual
references.)
* `audio`: Name of the audio file containing the usex in spoken form.
* `orig`: Original text, if the primary text of the usex or quotation is a translation.
* `origlang`: The language object of the original text. Mandatory if original text given. May be an etymology language.
* `origsc`: The script object of the original text. Autodetected if not given.
* `orignorm`: Normalized version of the original text (esp. for older languages where nonstandard spellings were
common).
* `orignormsc`: Script object of the normalized original text. If unspecified, use the script object given in `origsc`
if any, otherwise do script detection on the normalized original text. If "auto", do script detection
on the normalized text even if a script was specified in `origsc`.
* `origtr`: Transliteration of the original text. If unspecified, transliterate the normalized original text if
specified and not in a Latin script and transliterable, otherwise fall back to transliterating the original
text.
* `origts`: Transcription of the original text, for languages where the transliteration differs significantly from the
pronunciation.
* `origsubst`: String indicating substitutions to perform on the original text and normalization thereof prior to
transliterating them. Multiple substs are comma-separated and individual substs are of the form FROM//TO
where FROM is a Lua pattern and TO is a Lua replacement spec. (FROM/TO is also recognized if no // is
present in the substitution.)
* `origq`: If specified, a list of left qualifiers to display before the original text.
* `origqq`: If specified, a list of right qualifiers to display after the original text.
* `origref`: Reference text to display directly after the right qualifiers of the original text. (FIXME: Instead, this
should be actual references.)
* `source`: Source of the quotation, displayed in parens after the quotation text.
* `footer`: Footer displaying miscellaneous information, shown after the quotation. (Typically this should be in a
small font.)
* `nocat`: Suppress categorization.
* `noreq`: Suppress request for translation when no translation provided.
* `sortkey`: Sort key for categories.
* `brackets`: If specified, show a bracket at the end (used with brackets= in {{tl|quote-*}} templates, which show the
bracket at the beginning, to indicate a mention rather than a use).
* `class`: Additional CSS class surrounding the entire formatted text.
]==]
function export.format_usex(data)
local lang = data.lang
local termlang = data.termlang or lang
local translation = data.translation
local quote = data.quote
local lit = data.lit
local audio = data.audio
local source = data.source
local brackets = data.brackets
local footer = data.footer
local sortkey = data.sortkey
local noreq = data.noreq
local title
if data.pagename then -- for testing, doc pages, etc.
title = mw.title.new(data.pagename)
if not title then
error(("Bad value for `data.pagename`: '%s'"):format(data.pagename))
end
else
title = mw.title.getCurrentTitle()
end
--[[
if title.nsText == "Reconstruction" or lang:hasType("reconstructed") then
error("Reconstructed languages and reconstructed terms cannot have usage examples, as we have no record of their use.")
end
]]
if lit then
lit = "(herfiyen, “" .. span(css_classes.literally, lit) .. "”)"
end
if source then
source = wrap("span", "jeder", " — (" .. source .. ")", nil, "font-size: 0.85em")
end
if footer then
footer = span(css_classes.footer, footer)
end
local example_type = quote and "jêgirtin" or "nimûne" -- used in error messages and categories
local categories = {}
local usex_obj = process_usex_text {
lang = lang,
termlang = termlang,
usex = data.usex,
sc = data.sc,
tr = data.transliteration,
ts = data.transcription,
norm = data.normalization,
normsc = data.normsc,
subst = data.subst,
quote = data.quote,
title = title,
q = data.q,
qq = data.qq,
ref = data.ref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
}
local orig_obj = data.orig and process_usex_text {
lang = data.origlang,
-- Any categories derived from the original text should use the language of the main text or the term inside it,
-- not the language of the original text.
termlang = termlang,
usex = data.orig,
sc = data.origsc,
tr = data.origtr,
ts = data.origts,
norm = data.orignorm,
normsc = data.orignormsc,
subst = data.origsubst,
quote = data.quote,
title = title,
q = data.origq,
qq = data.origqq,
ref = data.origref,
nocat = data.nocat,
categories = categories,
example_type = example_type,
} or nil
if translation == "-" then
translation = nil
table.insert(categories, ("%s %ss with omitted translation"):format(lang:getFullName(),
example_type))
elseif translation then
translation = span(css_classes.translation, translation)
elseif not noreq then
local langcode = lang:getFullCode()
local origlangcode = data.origlang and data.origlang:getFullCode()
if langcode ~= "ku" and langcode ~= "navz" and langcode ~= "und" and origlangcode ~= "ku" then
-- add trreq category if translation is unspecified and language is not english, translingual or
-- undetermined
table.insert(categories, ("Daxwazên tercimekirina %syan bi %s"):format(example_type, lang:getCanonicalName()))
if quote then
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê jêgirtinê binivîsin)</small>"
else
translation = "<small>(ji kerema xwe tercimeya kurmancî ya vê nimûneyê binivîsin)</small>"
end
end
end
local function generate_inline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
if source then
ins(" " .. source)
end
ins(format_audio(audio))
local function insert_annotations(obj)
if obj.norm then
ins(" " .. obj.norm)
end
if obj.tr or obj.ts then
ins(" ―")
if obj.tr then
ins(" " .. obj.tr)
end
if obj.ts then
ins(" " .. obj.ts)
end
end
end
insert_annotations(usex_obj)
if orig_obj then
ins(" (")
ins("[" .. original_text .. orig_obj.usex .. "]")
insert_annotations(orig_obj)
ins(")")
end
if translation then
ins(" ― " .. translation)
end
if lit then
ins(" " .. lit)
end
if footer then
ins(" " .. footer)
end
if data.brackets then
ins("]")
end
return table.concat(result)
end
local function generate_multiline_usex()
local result = {}
local function ins(text)
table.insert(result, text)
end
ins(usex_obj.usex)
ins(source)
ins(format_audio(audio))
local any_usex_annotations = usex_obj.tr or usex_obj.ts or usex_obj.norm or translation or lit
local any_orig_annotations = orig_obj and (orig_obj.tr or orig_obj.ts or orig_obj.norm)
if any_usex_annotations or orig_obj or footer then
ins("<dl>")
local function insert_dd(text)
if text then
ins("<dd>")
ins(text)
if data.brackets then
ins(BRACKET_SUB)
end
ins("</dd>")
end
end
insert_dd(usex_obj.norm)
insert_dd(usex_obj.tr)
insert_dd(usex_obj.ts)
if orig_obj then
insert_dd("[" .. original_text .. orig_obj.usex .. "]")
if any_orig_annotations then
ins("<dd><dl>")
insert_dd(orig_obj.norm)
insert_dd(orig_obj.tr)
insert_dd(orig_obj.ts)
ins("</dl></dd>")
end
end
insert_dd(translation)
insert_dd(lit)
if footer then
if any_usex_annotations then
ins("<dd><dl>")
end
insert_dd(footer)
if any_usex_annotations then
ins("</dl></dd>")
end
end
ins("</dl>")
elseif data.brackets then
ins(BRACKET_SUB)
end
result = table.concat(result)
if data.brackets then
result = result:gsub("^(.*)" .. BRACKET_SUB, "%1]"):gsub(BRACKET_SUB, "")
end
return result
end
local is_inline
if data.inline == "auto" then
result = generate_inline_usex()
if get_character_width(convert_to_raw_text(result)) > MAX_INLINE_WIDTH then
result = generate_multiline_usex()
is_inline = false
else
is_inline = true
end
elseif data.inline then
result = generate_inline_usex()
is_inline = true
else
result = generate_multiline_usex()
is_inline = false
end
local class = quote and css_classes.container_quotation or css_classes.container_ux
if data.class then
class = class .. " " .. data.class
end
result = (is_inline and span or div)(class, result)
return result .. require("Module:utilities").format_categories(categories, lang, sortkey)
end
return export
57dxkx8ywqyxtjfhs5aiqo81syf6qg6
Wîkîferheng:Dîwan/2026
4
1327120
6277075
6276817
2026-07-24T09:20:21Z
Wikihez
7344
6277075
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
emddc5be8oly5gbehjb59dyzux497oj
6277076
6277075
2026-07-24T09:32:22Z
Wikihez
7344
/* Modul:quote */
6277076
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[:e:Template:quote-book]] / [[Şablon:jêder-kitêb/ceribandin]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
5yxnrkd741pja6ngzsfcjdr4oexa9ao
6277077
6277076
2026-07-24T09:32:31Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6277077
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[:en:Template:quote-book]] / [[Şablon:jêder-kitêb/ceribandin]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
4cj0zp3ue5ij4bpc83218ay52krpw2w
6277080
6277077
2026-07-24T09:41:33Z
Bikarhêner
3719
/* Modul:quote */ Bersiv
6277080
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[:en:Template:quote-book]] / [[Şablon:jêder-kitêb/ceribandin]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
:Bi min bila yê mewcûd jî bimîne, ew jî şiklen xweş û baştir e. Feqet bi min jî yên îngilîzî rehettir in, eger rastkirina yê mewcûd zehmet be, em rasterast wan ji xwe re bînin û tercime bikin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:41, 24 tîrmeh 2026 (UTC)
6ur470xkjry7oym9xbtam2x399154yo
6277081
6277080
2026-07-24T09:41:45Z
Bikarhêner
3719
/* Modul:quote */
6277081
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[:en:Template:quote-book]] / [[Şablon:jêder-kitêb/ceribandin]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
::Bi min bila yê mewcûd jî bimîne, ew jî şiklen xweş û baştir e. Feqet bi min jî yên îngilîzî rehettir in, eger rastkirina yê mewcûd zehmet be, em rasterast wan ji xwe re bînin û tercime bikin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:41, 24 tîrmeh 2026 (UTC)
i6ruk512k8n90jdqdh43p88dewg95o1
6277082
6277081
2026-07-24T09:44:13Z
Wikihez
7344
/* Modul:quote */
6277082
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[Wîkîferheng:ceribandin/nimûne#Îngilîzî]] / [[Wîkîferheng:ceribandin/nimûne#Kurmancî]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
::Bi min bila yê mewcûd jî bimîne, ew jî şiklen xweş û baştir e. Feqet bi min jî yên îngilîzî rehettir in, eger rastkirina yê mewcûd zehmet be, em rasterast wan ji xwe re bînin û tercime bikin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:41, 24 tîrmeh 2026 (UTC)
jyyzzrborsm4r5qtrxikr0l7yd0b8v2
6277083
6277082
2026-07-24T09:59:29Z
Wikihez
7344
/* Modul:quote */ Bersiv
6277083
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[Wîkîferheng:ceribandin/nimûne#Îngilîzî]] / [[Wîkîferheng:ceribandin/nimûne#Kurmancî]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
::Bi min bila yê mewcûd jî bimîne, ew jî şiklen xweş û baştir e. Feqet bi min jî yên îngilîzî rehettir in, eger rastkirina yê mewcûd zehmet be, em rasterast wan ji xwe re bînin û tercime bikin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:41, 24 tîrmeh 2026 (UTC)
:::Ez êvarê ve dixebitim, min nêzîk kiribû lê min got belkî hûn qane bibin lê xuya ye hûn ê hîn serê min gelek biêşînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:59, 24 tîrmeh 2026 (UTC)
m7qfykf5f93ejh62neqi5e1k9ozbqw1
6277084
6277083
2026-07-24T09:59:59Z
Wikihez
7344
/* Modul:quote */
6277084
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[:en:Template:quote-book]] / [[Şablon:jêder-kitêb/ceribandin]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
::Bi min bila yê mewcûd jî bimîne, ew jî şiklen xweş û baştir e. Feqet bi min jî yên îngilîzî rehettir in, eger rastkirina yê mewcûd zehmet be, em rasterast wan ji xwe re bînin û tercime bikin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:41, 24 tîrmeh 2026 (UTC)
:::Ez êvarê ve dixebitim, min nêzîk kiribû lê min got belkî hûn qane bibin lê xuya ye hûn ê hîn serê min gelek biêşînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:59, 24 tîrmeh 2026 (UTC)
8o0bgcjvd8bmh2d7e15s7mip6vwwisk
Şablon:Ç:ota:Stachowski:1975-1986
10
1357516
6276929
6240133
2026-07-23T12:34:47Z
Wikihez
7344
6276929
wikitext
text/x-wiki
{{cite-book
|de
|last=Stachowski
|first=Stanisław
|authorlink=pl:Stanisław Stachowski
|title=Studien über die arabischen Lehnwörter im Osmanisch-Türkischen
|entry={{{head|{{{1|}}}}}}
|location=Wrocław
|publisher=Academy Press
|year={{#switch:{{{volume}}}|I|1=1975|II|2=1977|III|3=1981|IV|4=1986|#default=1975–1986}}
|pageparam=page
|series=Prace Komisji Orientalistycznej
|seriesvolume={{#switch:{{{volume}}}|I|1=13|II|2=15|III|3=17|IV|4=19}}
|propagateparams=volume
|allowparams=head,1
}}<noinclude>{{refcat}}</noinclude>
mn7d7jwt96zm7ds3yqlpetfk3lgxu1i
6277051
6276929
2026-07-24T02:20:58Z
Wikihez
7344
Guhartoya [[Special:Diff/6276929|6276929]] yê [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|gotûbêj]]) şûnde kir
6277051
wikitext
text/x-wiki
{{#invoke:quote|call_template
|template=cite-book
|de
|last=Stachowski
|first=Stanisław
|authorlink=pl:Stanisław Stachowski
|title=Studien über die arabischen Lehnwörter im Osmanisch-Türkischen
|entry={{{head|{{{1|}}}}}}
|location=Wrocław
|publisher=Academy Press
|year={{#switch:{{{volume}}}|I|1=1975|II|2=1977|III|3=1981|IV|4=1986|#default=1975–1986}}
|pageparam=page
|series=Prace Komisji Orientalistycznej
|seriesvolume={{#switch:{{{volume}}}|I|1=13|II|2=15|III|3=17|IV|4=19}}
|propagateparams=volume
|allowparams=head,1
}}<noinclude>{{refcat}}</noinclude>
1j6a5mtmsw8blnhtw6wg0rl46uydqmq
Modul:quote
828
1357519
6277028
6276898
2026-07-24T01:29:48Z
Wikihez
7344
Restored revision 6240129 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277028
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(format_multivalued_annotated_text(authorobjs))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(formatted_text)
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(author)
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep("anonymous" .. anonymous_suffix)
elseif prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = "<dl><dd>" .. format_usex(usex_data) .. "</dd></dl>"
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, '<div class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, nil, other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args))
insert(parts, "</div>")
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
6o2boijssnzfvdyknbvpnnbbo2zzu5x
6277039
6277028
2026-07-24T01:33:48Z
Wikihez
7344
6277039
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(format_multivalued_annotated_text(authorobjs))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(formatted_text)
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(author)
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep("anonymous" .. anonymous_suffix)
elseif prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = "<dl><dd>" .. format_usex(usex_data) .. "</dd></dl>"
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, '<div class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, nil, other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args))
insert(parts, "</div>")
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
8gl5bda4nguaen4r31upqcn2p3sj5dj
6277041
6277039
2026-07-24T01:41:28Z
Wikihez
7344
6277041
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(format_multivalued_annotated_text(authorobjs))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(formatted_text)
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(author)
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep("anonymous" .. anonymous_suffix)
elseif prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite, source)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
source = source
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = format_usex(usex_data)
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls)))
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
fy75ljhv9cjn35q6ths5e4pvpydd9u8
6277044
6277041
2026-07-24T02:01:21Z
Wikihez
7344
6277044
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
-- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a
-- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting).
local function j_lazim(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(j_lazim(format_multivalued_annotated_text(authorobjs)))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(j_lazim(formatted_text))
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(j_lazim(author))
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep(j_lazim("anonymous" .. anonymous_suffix))
else
entities = j_lazim(entities)
if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
coauthors = j_lazim(coauthors)
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
role = j_lazim(role)
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
actor = j_lazim(actor)
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
artist = j_lazim(artist)
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
feat = j_lazim(feat)
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
others = j_lazim(others)
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
by = j_lazim(by)
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable
-- we wrap; origdate=, date_published= and the newversion date are left unwrapped.
formatted_date = j_lazim(formatted_date)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite, source)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
source = source
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = format_usex(usex_data)
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls)))
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
1p95orqqew96wzmn23a5gj8x27auxjg
6277071
6277044
2026-07-24T08:00:49Z
Wikihez
7344
6277071
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
-- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a
-- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting).
local function j_lazim(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(j_lazim(format_multivalued_annotated_text(authorobjs)))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(j_lazim(formatted_text))
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(j_lazim(author))
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep(j_lazim("anonymous" .. anonymous_suffix))
else
entities = j_lazim(entities)
if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
coauthors = j_lazim(coauthors)
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
role = j_lazim(role)
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
actor = j_lazim(actor)
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
artist = j_lazim(artist)
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
feat = j_lazim(feat)
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
others = j_lazim(others)
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
by = j_lazim(by)
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable
-- we wrap; origdate=, date_published= and the newversion date are left unwrapped.
formatted_date = j_lazim(formatted_date)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3,nivîskar"},
{"title", "4,sernav"},
{"url", "5"},
{"year", "2,sal"},
{"page", "6,rûpel,r"},
{"text", "7,jêgirtin"},
{"t", "8,w,werger,tercime"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite, source)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
source = source
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = format_usex(usex_data)
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls)))
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
rpml8qp82ulvkn5fbey6oc5ga6detza
6277073
6277071
2026-07-24T08:26:38Z
Wikihez
7344
Restored revision 6277044 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277073
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
-- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a
-- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting).
local function j_lazim(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(j_lazim(format_multivalued_annotated_text(authorobjs)))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(j_lazim(formatted_text))
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(j_lazim(author))
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep(j_lazim("anonymous" .. anonymous_suffix))
else
entities = j_lazim(entities)
if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
coauthors = j_lazim(coauthors)
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
role = j_lazim(role)
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
actor = j_lazim(actor)
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
artist = j_lazim(artist)
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
feat = j_lazim(feat)
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
others = j_lazim(others)
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
by = j_lazim(by)
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable
-- we wrap; origdate=, date_published= and the newversion date are left unwrapped.
formatted_date = j_lazim(formatted_date)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite, source)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
source = source
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = format_usex(usex_data)
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls)))
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
1p95orqqew96wzmn23a5gj8x27auxjg
6277074
6277073
2026-07-24T09:04:31Z
Wikihez
7344
6277074
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
-- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a
-- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting).
local function j_lazim(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(j_lazim(format_multivalued_annotated_text(authorobjs)))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(j_lazim(formatted_text))
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(j_lazim(author))
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep(j_lazim("anonymous" .. anonymous_suffix))
else
entities = j_lazim(entities)
if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
coauthors = j_lazim(coauthors)
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
role = j_lazim(role)
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
actor = j_lazim(actor)
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
artist = j_lazim(artist)
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
feat = j_lazim(feat)
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
others = j_lazim(others)
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
by = j_lazim(by)
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable
-- we wrap; origdate=, date_published= and the newversion date are left unwrapped.
formatted_date = j_lazim(formatted_date)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
["hrf"] = {alias_of = "lit"},
t = true,
translation = alias_of_t,
["tercime"] = alias_of_t,
["werger"] = alias_of_t,
["w"] = alias_of_t,
gloss = alias_of_t,
lb = true,
["e"] = {alias_of = "lb"},
brackets = boolean,
-- original quote params
origtext = true,
["orîjmetn"] = {alias_of = "origtext"},
origtr = true,
["orîjtr"] = {alias_of = "origtr"},
origts = true,
["orîjts"] = {alias_of = "origts"},
orignorm = true,
["orîjnorm"] = {alias_of = "orignorm"},
origsc = script,
["orîjsc"] = {alias_of = "origsc"},
orignormsc = script,
["orîjnormsc"] = {alias_of = "orignormsc"},
origsubst = true,
["orîjsubst"] = {alias_of = "origsubst"},
origlb = true,
["orîje"] = {alias_of = "origlb"},
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite, source)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
source = source
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = format_usex(usex_data)
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls)))
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
8pr0qhf0vxgkd3hzc6i6m369vhcpl8p
6277078
6277074
2026-07-24T09:36:55Z
Wikihez
7344
Restored revision 6277028 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277078
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(format_multivalued_annotated_text(authorobjs))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(formatted_text)
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(author)
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep("anonymous" .. anonymous_suffix)
elseif prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = "<dl><dd>" .. format_usex(usex_data) .. "</dd></dl>"
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, '<div class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, nil, other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args))
insert(parts, "</div>")
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
6o2boijssnzfvdyknbvpnnbbo2zzu5x
6277085
6277078
2026-07-24T10:00:22Z
Wikihez
7344
Restored revision 6277074 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277085
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex/ceribandin"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
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 find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
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 ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
-- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a
-- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting).
local function j_lazim(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(j_lazim(format_multivalued_annotated_text(authorobjs)))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(j_lazim(formatted_text))
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(j_lazim(author))
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep(j_lazim("anonymous" .. anonymous_suffix))
else
entities = j_lazim(entities)
if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
coauthors = j_lazim(coauthors)
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("in ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("in ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
role = j_lazim(role)
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
actor = j_lazim(actor)
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
mainauthor = j_lazim(mainauthor)
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "en") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (in " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "in " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "in " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
artist = j_lazim(artist)
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
feat = j_lazim(feat)
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
others = j_lazim(others)
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
by = j_lazim(by)
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable
-- we wrap; origdate=, date_published= and the newversion date are left unwrapped.
formatted_date = j_lazim(formatted_date)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
["hrf"] = {alias_of = "lit"},
t = true,
translation = alias_of_t,
["tercime"] = alias_of_t,
["werger"] = alias_of_t,
["w"] = alias_of_t,
gloss = alias_of_t,
lb = true,
["e"] = {alias_of = "lb"},
brackets = boolean,
-- original quote params
origtext = true,
["orîjmetn"] = {alias_of = "origtext"},
origtr = true,
["orîjtr"] = {alias_of = "origtr"},
origts = true,
["orîjts"] = {alias_of = "origts"},
orignorm = true,
["orîjnorm"] = {alias_of = "orignorm"},
origsc = script,
["orîjsc"] = {alias_of = "origsc"},
orignormsc = script,
["orîjnormsc"] = {alias_of = "orignormsc"},
origsubst = true,
["orîjsubst"] = {alias_of = "origsubst"},
origlb = true,
["orîje"] = {alias_of = "origlb"},
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite, source)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
source = source
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = format_usex(usex_data)
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls)))
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
8pr0qhf0vxgkd3hzc6i6m369vhcpl8p
Wîkîferheng:Xebat
4
1357857
6276965
6276886
2026-07-23T20:00:21Z
Wikihez
7344
6276965
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start.js/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Gadget-linkify ==
* [[Mediawiki:Gadget-linkify.js]] ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
= Moda tarî =
== MediaWiki:Newarticletext ==
* [[MediaWiki:Newarticletext]]: Divê rengên wê were rastkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:dara kategoriyan ==
* [[Modul:dara kategoriyan]] link_box —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== mane? ==
* {{ş|mane?}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== bêçavkanî ==
* {{ş|bêçavkanî}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== de-tew-nerm-ern ==
* {{ş|de-tew-nerm-ern}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
cmd4sngum8pxnrx6rsp7nkb3hspvycg
Bikarhêner:Wikihez/ceribandin
2
1357863
6277002
6276755
2026-07-23T23:52:43Z
Wikihez
7344
6277002
wikitext
text/x-wiki
https://quarry.wmcloud.org/query/107636
{| class="wikitable sortable"
!page_title
|-
|mirmir
|-
|nêvgir
|-
|fir_fir
|-
|pir_pir
|-
|belakir
|-
|navbir
|-
|alahilgir
|-
|kindir
|-
|kefgir
|-
|kerankgir
|-
|piranîgir
|-
|gîvir
|-
|şivir
|-
|çapgir
|-
|avbir
|-
|babir
|-
|bêmîratgir
|-
|berîkbir
|-
|birqgir
|-
|kevirbir
|-
|alagir
|-
|bigir
|-
|bigiregir
|-
|bêalgir
|-
|bêlagir
|-
|birûskgir
|-
|binavbir
|-
|bêpegir
|-
|girgir
|-
|lixwegir
|-
|şûngir
|-
|nedir
|-
|têcir
|-
|mişqir
|-
|mubeşir
|-
|enasir
|-
|arê_sifir
|-
|hir_hir
|-
|teyrgir
|-
|ahûgir
|-
|askgir
|-
|bazgir
|-
|bêşgir
|-
|bihanegir
|-
|cemedgir
|-
|çargir
|-
|çorgir
|-
|daraşgir
|-
|dersgir
|-
|destnivêjgir
|-
|dijgir
|-
|diyetgir
|-
|dostgir
|-
|êrişgir
|-
|faîzgir
|-
|fermangir
|-
|filmgir
|-
|firîwgir
|-
|fitregir
|-
|hêcetgir
|-
|heştlinggir
|-
|hicûmgir
|-
|hizbgir
|-
|îcargir
|-
|irtişagir
|-
|kempgir
|-
|kergedengir
|-
|bikirêgir
|-
|kundgir
|-
|lagir
|-
|mamizgir
|-
|marmasîgir
|-
|meaşgir
|-
|mesajgir
|-
|milgir
|-
|miqawelegir
|-
|nefeqegir
|-
|olamgir
|-
|pelamargir
|-
|perûgir
|-
|peyamgir
|-
|peymangir
|-
|qalibgir
|-
|qebalegir
|-
|qergir
|-
|qerqewilgir
|-
|qertelgir
|-
|qeşagir
|-
|refgir
|-
|rejîmgir
|-
|ribagir
|-
|rûmetgir
|-
|selefgir
|-
|semanekgir
|-
|siragir
|-
|siyanetgir
|-
|stîpendgir
|-
|sûretgir
|-
|şahîngir
|-
|şevgir
|-
|taretgir
|-
|tebgir
|-
|wanegir
|-
|werşeqgir
|-
|yekîtîgir
|-
|zekatgir
|-
|zewqgir
|-
|damargir
|-
|denggir
|-
|dilkafir
|-
|direfşgir
|-
|emanetgir
|-
|encamgir
|-
|gewdegir
|-
|gulaşgir
|-
|heyfgir
|-
|hisgir
|-
|hûrgir
|-
|kişwergir
|-
|pangir
|-
|pêdagir
|-
|radestgir
|-
|rikgir
|-
|serbazgir
|-
|teslîmgir
|-
|tolgir
|-
|xatirgir
|-
|dengbir
|-
|jehrbir
|-
|kincbir
|-
|mehrbir
|-
|paşbir
|-
|vebir
|-
|hempeywir
|-
|mîzokbir
|-
|dorgir
|-
|fêrgir
|-
|hişbir
|-
|namebir
|-
|razgir
|-
|serbangir
|-
|dilşadtir
|-
|giramîgir
|-
|kokgir
|-
|şertgir
|-
|xweysebir
|-
|deryagir
|-
|dilpir
|-
|gavbir
|-
|lêgir
|-
|paşmagir
|-
|pêkir
|-
|qelembir
|-
|gir_gir
|-
|girsegir
|-
|kumbir
|-
|meylgir
|-
|pir_gir
|-
|salgir
|-
|destbir
|-
|jêgir
|-
|katgir
|-
|koşgir
|-
|lêwergir
|-
|malgir
|-
|nerîtgir
|-
|pêvir
|-
|bêpêgir
|-
|çavgir
|-
|dalgir
|-
|dîlgir
|-
|xweyfikir
|-
|lêkvegir
|-
|pêbir
|-
|pêlbir
|-
|pêlgir
|-
|eşîrgir
|-
|gramgir
|-
|hesabgir
|-
|jibergir
|-
|kevjalgir
|-
|nihênîgir
|-
|rekehgir
|-
|tevdîrgir
|-
|hêlgir
|-
|bîçebir
|-
|rajegir
|-
|dewsgir
|-
|xercgir
|-
|cilgir
|-
|çaxgir
|-
|şeytanokgir
|-
|dorlêgir
|-
|giramnegir
|-
|koşegir
|-
|pêsirgir
|-
|spardegir
|-
|bacgir
|-
|partîgir
|-
|barhilgir
|-
|eylogir
|-
|gempir
|-
|piştstûgir
|-
|zîqûzir
|-
|bimîratgir
|-
|êşbir
|-
|avgir
|-
|binesir
|-
|bipegir
|-
|neynûkbir
|-
|golevir
|-
|hewagir
|-
|heyecangir
|-
|kobragir
|-
|mizgîngir
|-
|pepûkgir
|-
|restorangir
|-
|temîgir
|-
|balindegir
|-
|hestbir
|-
|gumangir
|-
|pêşvebir
|-
|pirranîgir
|-
|şêkir
|-
|sersîndir
|-
|semawir
|-
|qerzgir
|-
|resimgir
|-
|kiringir
|-
|yên_ji_me_çêtir
|-
|destheqwergir
|-
|destmizwergir
|-
|paqir
|-
|bahwir
|-
|bawir
|-
|cahtir
|-
|cehtir
|-
|ce'tir
|-
|cemahir
|-
|cemawir
|-
|çîxir
|-
|dehl_û_dir
|-
|deyingir
|-
|deyîngir
|-
|deyinwergir
|-
|deyînwergir
|-
|deynwergir
|-
|itir
|-
|fotografgir
|-
|heyir
|-
|Idir
|-
|Ixdir
|-
|Îxdir
|-
|kelegir
|-
|qelegir
|-
|kutir
|-
|leşgir
|-
|lîtir
|-
|micewwir
|-
|mucewir
|-
|mucewwir
|-
|mikir
|-
|mîraswergir
|-
|musafir
|-
|muçewergir
|-
|mûçegir
|-
|mûçewergir
|-
|patoxwir
|-
|psîkiyatir
|-
|zirbehir
|-
|rêhbir
|-
|serokleşkir
|-
|serleşkir
|-
|serekleşkir
|-
|Skenderê_Gir
|-
|şire-şir
|-
|şirşir
|-
|tefekkir
|-
|Xizir
|-
|xeşir
|-
|yê_ji_me_çêtir
|-
|yêd_ji_me_çêtir
|-
|yêt_ji_me_çêtir
|-
|zirzir
|-
|zirezir
|-
|zir-zir
|-
|zuxir
|-
|zexir
|-
|pencemohir
|-
|pencemuhir
|-
|zêringir
|-
|hûngir
|-
|mobaşir
|-
|ekir
|-
|ghir
|-
|pendir
|-
|ewdir
|-
|xeyir
|-
|qirecir
|-
|zire-zir
|-
|cantir
|-
|nepêgir
|-
|berbir
|-
|nebîrbir
|-
|rastbir
|-
|laşgir
|-
|serbarbir
|-
|kompiyutir
|-
|hevnegir
|-
|nepartîgir
|-
|bi_giregir
|-
|behanegir
|-
|direwşgir
|-
|'amir
|-
|eşîretgir
|-
|giramgir
|-
|hesêbgir
|-
|kêvjalegir
|-
|nihêngir
|-
|rikegir
|-
|sûdegir
|-
|tedbîrgir
|-
|xwînfir
|-
|qamir
|-
|werbir
|-
|cetir
|-
|birevebir
|-
|hûrnir
|-
|kompîtir
|-
|lihevkir
|-
|mihecir
|-
|qadir
|-
|ronakbir
|-
|tawanbarkir
|-
|tevdir
|-
|ticir
|-
|xurdekir
|-
|zarvekir
|-
|kamir
|-
|kîregir
|-
|niştir
|-
|alhilgir
|-
|menestir
|-
|çizir
|-
|meçêtir
|-
|ardê_sifir
|-
|befirgir
|-
|neagirgir
|-
|neliwir
|-
|şeşagir
|-
|birrêvebir
|-
|xinzir
|-
|yên_ji_me_kêmtir
|-
|ji_me_çêtir
|-
|jê_pêştir
|-
|nederbir
|-
|kifir
|-
|barhelgir
|-
|gapir
|-
|hendir
|-
|mire-mir
|-
|mir-mir
|-
|nêbir
|-
|tekerrir
|-
|zîvir
|-
|pandir
|-
|natir
|-
|hêjir
|-
|bagir
|-
|qedrnegir
|-
|hiştir
|-
|leşikir
|-
|kerrankgir
|-
|perrûgir
|-
|balafir
|-
|paşgir
|-
|pêşgir
|-
|befir
|-
|pir
|-
|qantir
|-
|hêstir
|-
|hêsir
|-
|şekir
|-
|masîgir
|-
|vir
|-
|agir
|-
|danegir
|-
|gir
|-
|xir
|-
|emir
|-
|hêştir
|-
|hindir
|-
|şair
|-
|lawir
|-
|çepgir
|-
|qedir
|-
|gawir
|-
|kafir
|-
|sir
|-
|hizir
|-
|bir
|-
|hibir
|-
|kevir
|-
|sinir
|-
|bivir
|-
|sabir
|-
|berindir
|-
|oxir
|-
|bizir
|-
|sifir
|-
|komir
|-
|qatir
|-
|rêbir
|-
|wir
|-
|ev_dir
|-
|şefir
|-
|bişir
|-
|êgir
|-
|soldir
|-
|birêvebir
|-
|rêvebir
|-
|çadir
|-
|peyambir
|-
|uzir
|-
|piştgir
|-
|xatir
|-
|cîgir
|-
|dir
|-
|kepir
|-
|koçir
|-
|nazir
|-
|qebir
|-
|kir
|-
|dilkevir
|-
|apogir
|-
|catir
|-
|darbir
|-
|behir
|-
|beḧir
|-
|tesewir
|-
|heşir
|-
|golgir
|-
|wênegir
|-
|tir
|-
|zivir
|-
|rexnegir
|-
|mehir
|-
|cawbir
|-
|balefir
|-
|cobir
|-
|edir
|-
|'edir
|-
|xirexir
|-
|umir
|-
|'imir
|-
|'emir
|-
|'umir
|-
|hoştir
|-
|ḧoştir
|-
|hundir
|-
|windir
|-
|wundir
|-
|gûbir
|-
|xedir
|-
|afir
|-
|şa'ir
|-
|şahir
|-
|şe'ir
|-
|şi'ir
|-
|şîhir
|-
|şeir
|-
|şî'ir
|-
|şiir
|-
|şîir
|-
|şehir
|-
|şihir
|-
|histir
|-
|kadir
|-
|nobegir
|-
|nobetgir
|-
|bertîlgir
|-
|bertîlwergir
|-
|rişwetgir
|-
|rişwetwergir
|-
|bertîlxwir
|-
|rişwetxwir
|-
|hogir
|-
|cewir
|-
|fikir
|-
|hubir
|-
|ḧubir
|-
|ḧibir
|-
|qir
|-
|biwir
|-
|ewir
|-
|'ewir
|-
|hewir
|-
|barimtegir
|-
|kirêgir
|-
|sebir
|-
|tacir
|-
|beyindir
|-
|şikir
|-
|şukir
|-
|dagir
|-
|zir
|-
|kavir
|-
|axwir
|-
|pegir
|-
|jahir
|-
|jehir
|-
|mihacir
|-
|muhacir
|-
|fir
|-
|çayir
|-
|bohir
|-
|paxir
|-
|dewir
|-
|nevîçir
|-
|nevîçirçir
|-
|qesir
|-
|kumir
|-
|daşir
|-
|hilgir
|-
|helgir
|-
|gelendir
|-
|gelindir
|-
|şafir
|-
|qehir
|-
|şemendefir
|-
|pêwir
|-
|şatir
|-
|peywir
|-
|gewir
|-
|cildir
|-
|fitir
|-
|mamir
|-
|izir
|-
|'izir
|-
|'uzir
|-
|giregir
|-
|fecir
|-
|ragir
|-
|bergir
|-
|terefgir
|-
|layengir
|-
|algir
|-
|wergir
|-
|alîgir
|-
|mohir
|-
|muhir
|-
|cihgir
|-
|cîhgir
|-
|cêgir
|-
|şêwir
|-
|axir
|-
|bikir
|-
|biqedir
|-
|tivir
|-
|çir
|-
|çimzir
|-
|kufir
|-
|tiraktir
|-
|tirektir
|-
|traktir
|-
|trektir
|-
|cisir
|-
|dezgir
|-
|desgir
|-
|destgir
|-
|tiretir
|-
|mêhir
|-
|neşir
|-
|zikir
|-
|xişir
|-
|tevir
|-
|kengir
|-
|leşkir
|-
|sîmir
|-
|rawir
|-
|deyngir
|-
|tewir
|-
|zixir
|-
|rêgir
|-
|hicir
|-
|şireşir
|-
|mukir
|-
|zeqir
|-
|micewir
|-
|naxir
|-
|esir
|-
|qahir
|-
|xelatgir
|-
|hawir
|-
|awir
|-
|avir
|-
|Îdir
|-
|nifir
|-
|rojîgir
|-
|hezir
|-
|teyir
|-
|zahir
|-
|pejir
|-
|qitmir
|-
|kitmir
|-
|nesir
|-
|xemir
|-
|zecir
|-
|wayir
|-
|navgir
|-
|tekerir
|-
|notir
|-
|sawir
|-
|xirûcir
|-
|kundir
|-
|Şakir
|-
|çewir
|-
|mîrasgir
|-
|paşmakgir
|-
|xwir
|-
|tûmir
|-
|etir
|-
|cebir
|-
|sahir
|-
|nehir
|-
|amir
|-
|gepir
|-
|atir
|-
|azir
|-
|pengir
|-
|hafir
|-
|avdir
|-
|ahir
|-
|pêmir
|-
|sikir
|-
|çixir
|-
|tefekir
|-
|manastir
|-
|kehir
|-
|cîhangir
|-
|Xidir
|-
|cûbir
|-
|hirhir
|-
|cir
|-
|çandir
|-
|facir
|-
|kewgir
|-
|zebir
|-
|tagir
|-
|helîkopterfir
|-
|tîtavgir
|-
|muçegir
|-
|kotir
|-
|zegir
|-
|misafir
|-
|kêndir
|-
|kendir
|-
|cudagir
|-
|qerwaşgir
|-
|navçegir
|-
|sitir
|-
|lezbir
|-
|av_û_agir
|-
|çetir
|-
|dozgir
|-
|baqir
|-
|bir_bir
|-
|hûndir
|-
|nezir
|-
|satir
|-
|tir_tir
|-
|canhêştir
|-
|firefir
|-
|ireir
|-
|çolbir
|-
|dehlûdir
|-
|asir
|-
|ḧêştir
|-
|micêwir
|-
|mîratgir
|-
|mîratwergir
|-
|sêhir
|-
|sîhir
|-
|şir-şir
|-
|cirecir
|-
|jêbir
|-
|jimeçêtir
|-
|miremir
|-
|nîşangir
|-
|qirqir
|-
|şilindir
|-
|xirxir
|-
|xirecir
|-
|kulindir
|-
|barbir
|-
|agirbir
|-
|serbir
|-
|şêlimdir
|-
|çîrokvegir
|-
|sadir
|-
|sîqûsir
|-
|gotir
|-
|êtir
|-
|fotogir
|-
|heştpêgir
|-
|kemîngir
|-
|rehîngir
|-
|rehînegir
|-
|xeracgir
|-
|bebir
|-
|pêşbir
|-
|meaşwergir
|-
|piştîhilgir
|-
|birebir
|-
|pezbir
|-
|hirehir
|-
|virevir
|-
|cir_cir
|-
|nirnir
|-
|cire-cir
|-
|kartêkir
|-
|bargir
|-
|berafir
|-
|margir
|-
|bindir
|-
|xweyxatir
|-
|kenargir
|-
|cemedbir
|-
|nealgir
|-
|debir
|-
|sûdgir
|-
|berdbir
|-
|imir
|-
|alahelgir
|-
|avakir
|-
|Milazgir
|-
|pêşdebir
|-
|qeşembir
|-
|badexwir
|-
|marxwir
|-
|zuhir
|-
|şekkir
|-
|xircir
|-
|exir
|-
|mêşgir
|-
|neynokbir
|-
|sewir
|-
|sergir
|-
|didanfir
|-
|şa‘ir
|-
|lepir
|-
|porbir
|-
|axbandir
|-
|kevirgir
|-
|teyr_borî_fisek_kir
|-
|çotir
|-
|pêxir
|-
|şûnhelgir
|-
|zêvzir
|-
|fîlmgir
|-
|sihir
|}
okfly4rxn3ycsly9qaa9k68247oi7gf
6277004
6277002
2026-07-23T23:54:26Z
Wikihez
7344
Restored revision 6276755 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277004
wikitext
text/x-wiki
==== Modul:zimanên etîmolojiyê====
{{Special:PrefixIndex/Modul:zimanên etîmolojiyê/|stripprefix=yes}}
==== Modul:etymology languages====
{{Special:PrefixIndex/Modul:etymology languages/|stripprefix=yes}}
==== Binêre herwiha ====
{{veşêre-ser}}
{| class="wikitable"
!wikilink
|-
|[[Pêvek:Bêçavkanî]]
|-
|[[Pêvek:Bêçavkanî/a]]
|-
|[[Pêvek:Bêçavkanî/a/alozbûn wekî wayra]]
|-
|[[Pêvek:Bêçavkanî/aloq]]
|-
|[[Pêvek:Bêçavkanî/alozbûn]]
|-
|[[Pêvek:Bêçavkanî/amerzandin]]
|-
|[[Pêvek:Bêçavkanî/asûm]]
|-
|[[Pêvek:Bêçavkanî/bester]]
|-
|[[Pêvek:Bêçavkanî/cewser]]
|-
|[[Pêvek:Bêçavkanî/derbest]]
|-
|[[Pêvek:Bêçavkanî/dogirme]]
|-
|[[Pêvek:Bêçavkanî/dolivandin]]
|-
|[[Pêvek:Bêçavkanî/dêziman]]
|-
|[[Pêvek:Bêçavkanî/girêmestî]]
|-
|[[Pêvek:Bêçavkanî/hesoskî]]
|-
|[[Pêvek:Bêçavkanî/hilinder]]
|-
|[[Pêvek:Bêçavkanî/ji barê ve]]
|-
|[[Pêvek:Bêçavkanî/kakilîk]]
|-
|[[Pêvek:Bêçavkanî/kedpîr]]
|-
|[[Pêvek:Bêçavkanî/kilûç]]
|-
|[[Pêvek:Bêçavkanî/nêremê]]
|-
|[[Pêvek:Bêçavkanî/nû]]
|-
|[[Pêvek:Bêçavkanî/oc bûn]]
|-
|[[Pêvek:Bêçavkanî/qomik]]
|-
|[[Pêvek:Bêçavkanî/rojol]]
|-
|[[Pêvek:Bêçavkanî/sîniyar]]
|-
|[[Pêvek:Bêçavkanî/tazîger]]
|-
|[[Pêvek:Bêçavkanî/tevilîn]]
|-
|[[Pêvek:Bêçavkanî/torecivîn]]
|-
|[[Pêvek:Bêçavkanî/zelîn]]
|-
|[[Pêvek:Bêçavkanî/şeebî]]
|}
{{veşêre-bin}}
gfr75qbiupuy9wa5ak4vpiarzfv0wpo
Wîkîferheng:ceribandin/nimûne
4
1357865
6276974
6276365
2026-07-23T21:33:34Z
Wikihez
7344
/* Lêkerw */
6276974
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku|c=h-gh}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder|ku|2=test|url=https://ku.wiktionary.org/wiki/bas%C3%BBr|jêgirtin=test}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
h7pdy3pi8qils3p3ojyabms7w4efedm
6276976
6276974
2026-07-23T21:52:22Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6276976
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku|c=h-gh}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
mdgijha5fnof1jrlpvgzkrn4fiz6a78
6276977
6276976
2026-07-23T21:52:58Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6276977
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
5c388p974wnz2i3ojka61n14jmq1zek
6276978
6276977
2026-07-23T21:53:10Z
Wikihez
7344
+nimûneya bikaranînê bi amûra « [[Mediawiki:Gadget-wikt.add-examples|Nimûne zêde bike]] » (v1.7).
6276978
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
qlkyp7b6joocjmbgwlws0g3rjo5wtoo
6277016
6276978
2026-07-24T00:39:57Z
Wikihez
7344
6277016
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
8zhfoed3bx4af9y65s4fpa6jwbq3jck
6277017
6277016
2026-07-24T00:40:24Z
Wikihez
7344
/* Lêker */
6277017
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test|tercime=testestp}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
7b9th6sriv87iqripwrex35jad449gl
6277018
6277017
2026-07-24T00:40:33Z
Wikihez
7344
/* Lêker */
6277018
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test|w=testestp}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
e969g1jb5ddwvrefgv58fu0u01tu5fk
6277019
6277018
2026-07-24T00:40:48Z
Wikihez
7344
/* Lêker */
6277019
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test|w=testestp}}
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
f70bweiiwyfmrjo06tqdbn09l5l4jqp
6277020
6277019
2026-07-24T00:41:55Z
Wikihez
7344
Restored revision 6277017 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277020
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test|tercime=testestp}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
7b9th6sriv87iqripwrex35jad449gl
6277022
6277020
2026-07-24T00:51:33Z
Wikihez
7344
/* {{ziman|en}} */
6277022
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|ku|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
2yakctnaxiv9re80jaf1tlw9htq6arq
6277023
6277022
2026-07-24T00:51:54Z
Wikihez
7344
+nimûneya bikaranînê bi amûra « [[Mediawiki:Gadget-wikt.add-examples|Nimûne zêde bike]] » (v1.7).
6277023
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|ku|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder|ku|2=test|url=https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:ceribandin/nim%C3%BBne#%7B%7Bziman%7Cku%7D%7D|jêgirtin=test}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
5l21avwdv5t11mfqaec867ptids25d1
6277024
6277023
2026-07-24T01:09:29Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6277024
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
s7dqbtq6793t323fuqr35jehp9k0tvh
6277025
6277024
2026-07-24T01:15:57Z
Wikihez
7344
Restored revision 6276978 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277025
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
#* {{jêder-websîte|en|sernav=گەورە|url=https://ku.wiktionary.org/w/index.php?title=%DA%AF%DB%95%D9%88%D8%B1%DB%95&oldid=5138292|sal=2023|tarîx=2023-02-08|jêgirtin=test}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
qlkyp7b6joocjmbgwlws0g3rjo5wtoo
6277026
6277025
2026-07-24T01:16:21Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6277026
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
# [[testsdfsdafdasfas]]
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
5c388p974wnz2i3ojka61n14jmq1zek
6277066
6277026
2026-07-24T07:33:10Z
Wikihez
7344
6277066
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
== {{ziman|ku}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku}}
# [[testsdfsdafdasfas]]
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
fu0jb3vxsj4pp20bzondxy0qogffr43
6277067
6277066
2026-07-24T07:36:29Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6277067
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
== {{ziman|ku}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku}}
# [[testsdfsdafdasfas]]
#* {{jêder-kitêb/ceribandin|ku|test=testjêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
j5p29cazs8ri9itebc7qay660uteh5n
6277068
6277067
2026-07-24T07:37:15Z
Wikihez
7344
6277068
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|test=test|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
== {{ziman|ku}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku}}
# [[testsdfsdafdasfas]]
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
o4wga86nr5k2nu2knhyeh5suyrujzvs
6277079
6277068
2026-07-24T09:38:31Z
Wikihez
7344
6277079
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
== {{ziman|ku}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku}}
# [[testsdfsdafdasfas]]
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
ft1r44ap09t6eq0s8ql1yntw0narakt
6277086
6277079
2026-07-24T10:03:30Z
Wikihez
7344
Restored revision 6277068 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]])
6277086
wikitext
text/x-wiki
{{/vê rêzê neguhêre}}
== {{ziman|en}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|en}}
# Maneya 1ê.
#: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}}
#* {{jêder-kovar/ceribandin|en|author=Justin Chang|authorlink=Justin Chang|title=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archiveurl=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|archivedate=28 June 2017|magazine=[[w:Variety (magazine)|Variety]]|date=25 May 2011|issn=0042-2738|oclc=960911146|passage=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}}
#* {{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}
#* {{jêder-malper/ceribandin|en|test=test|author=Meg Downey|title=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archiveurl=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|archivedate=24 June 2018|work=[[w:Polygon (website)|Polygon]]|date=7 May 2018|passage=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}}
== {{ziman|ku}} ==
=== Lêker ===
[[Wêne:Example.jpg|thumb|Mînak]]
{{lêker|ku}}
# [[testsdfsdafdasfas]]
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
#* {{jêder-pirtûk|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}}
#* {{jêder-websîte|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}}
#* {{jêder-kitêb|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}}
# Maneya 3an.
==== Bikaranîn ====
''Çend not li ser bikaranîna peyvê.''
==== Jê ====
{{kol3|ku|cure=Jê
|ceribandinvan
}}
==== Têkildar ====
* ''Peyvên ku têkiliya [[etîmolojîk]] bi peyvê re hene''.
=== Etîmolojî ===
''Çîroka tarîxî ya eslê kelîmeyê. Nimûneya bandor'': Ji {{deyn|ota|ku|باندره}}, {{q|ota|باكدیره}}, ({{hv|tr|bandıra}}) ji {{ji|vec|ku|bandéra}} ({{hv|it|bandiera}}) ji {{ji|gem|ku}} *''bandwa-'' ({{cotreh|ku|band|bend}}). Bide ber {{hv|fr|bandière}} (wek {{hv|en|banner}}), {{hv|el|παντιέρα}}.
==== Binêre herwiha ====
* ''Peyvên eleqadar lê belê têkiliya etîmolojîk nîne''.
* {{wpedî}}
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
* {{Z|en}}: {{W+|en|word}}
{{werger-bin}}
{{werger-ser|2|Maneya an}}
{{werger-bin}}
{{werger-bnr|Maneya 3an|kelîme}}
=== Navdêr ===
{{ku-tewîn-nav|mînak|mê}}
{{navdêr|ku|z=m}}
# mane 1
#: mînak 1-1
#: mînak 1-2
#* jêgirtin 1-1
#* jêgirtin 1-2
# mane 2
#: mînak 2-1
#: mînak 2-2
#* jêgirtin 2-1
#* jêgirtin 2-2
# mane 3
==== Werger ====
{{werger-ser|1|Maneya 1ê}}
{{werger-bin}}
{{werger-ser|2|Maneya 2an}}
{{werger-bin}}
{{werger-ser|3|Maneya 3an}}
{{werger-bin}}
=== Çavkanî ===
{{Çavkanî}}
* Çavkaniyên din li vir binivîse.
o4wga86nr5k2nu2knhyeh5suyrujzvs
xezete
0
1357889
6276961
6265655
2026-07-23T19:27:40Z
Wikihez
7344
6276961
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxizên]] [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çapkirin|çap kirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1993|sala weşandinê=1993|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=2026|nivîskar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
rknl3z8ot15xslhqytunsb2rym3jzgi
6276962
6276961
2026-07-23T19:28:50Z
Wikihez
7344
6276962
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxizên]] [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çapkirin|çap kirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1993|sala weşandinê=1993|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=2026|nivîskar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
ee1q7brrhjicb2j1r9iq20fjre16l4h
6276966
6276962
2026-07-23T20:01:23Z
Wikihez
7344
6276966
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxiz]]ên [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çap kirin|çapkirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1993|sala weşandinê=1993|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=2026|nivîskar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
f4xy23km73v8xlymdd4b82dj7ootvfp
6276967
6276966
2026-07-23T20:06:34Z
Wikihez
7344
6276967
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxiz]]ên [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çap kirin|çapkirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1993|sala weşandinê=1993|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
gmusy49fqpbrih4rhk7vwhz4apgn2zs
6276968
6276967
2026-07-23T20:07:28Z
Wikihez
7344
6276968
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxiz]]ên [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çap kirin|çapkirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1993|sala weşandinê=1993|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
h7r6ymjx23xwafljx9hopb5gnccijnr
6276970
6276968
2026-07-23T20:29:31Z
Wikihez
7344
6276970
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxiz]]ên [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çap kirin|çapkirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
jiqq7gtxhq7clz1p9p7kra9tqzuo0ya
6276997
6276970
2026-07-23T23:35:21Z
Wikihez
7344
/* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re
6276997
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|ẍezete}}
* {{ku-kîte}}
=== Navdêr ===
{{ku-tewîn-nav|xezete|mê|guherto=hemû}}
{{navdêr|ku|c=m}}
# [[ew|Ew]] [[kaxiz]]ên [[mezin]] ên ku [[her roj]] yan [[her]] [[hefte]] [[hatin|tên]] [[çap kirin|çapkirin]] û [[tê de]] [[xeber]]ên [[dinya]]yê, [[mesele]]yên [[siyaset]]ê, [[îlan]] û [[reklam]] [[hebûn|hene]].
#: {{alt-r|ku|ẍezete|qezete|gazete}}
#: {{hevmane|ku|cerîde|rojname}}
#: {{nm|ku|Bavo, ji min re '''xezeteyekê''' bistîne, ez lê binêrim ka di dinyayê de çi heye çi tine ye.}}
#* {{jêder-kitêb|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani |weşanxane=BRILL|ISBN=9789004754430|rûpel=383}}
#* {{jêder-kitêb|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}}
#* {{jêder-kitêb|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji % 70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}}
#* {{jêder-malper|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|sal=2021|tarîx=2021-05-04}}
#* {{jêder-kitêb|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}}
==== Jê ====
* {{g|ku|xezeteçî}}
==== Etîmolojî ====
Ji {{deyn|ota|ku|غزته|tr=ẍezete}} ji {{ji|it|ku|gazzetta}}.
==== Werger ====
{{werger-ser|kaxizên mezin ên ku her tên çapkirin|1}}
* {{Z|en}}: {{W|en|newspaper}}
* {{Z|tr}}: {{W|tr|gazete}}
{{werger-bin}}
199379bj8sloor3bbnz4mvyo23ozfq9
Modul:ku-tewîn-nav/form/text
828
1358141
6276949
6276883
2026-07-23T18:47:19Z
Ghybu
1393
mê û nêr
6276949
Scribunto
text/plain
local export = {}
local find = mw.ustring.find
local m_form_of_templates = require('Modul:form of/templates')
function export.caseName(declNoun, gender)
if find(declNoun, 'ên$') then return {'defConstr', n='pl'} end
if find(declNoun, 'inan$') then return {'indefObl', n='pl'} end
if find(declNoun, 'an$') then return {'defObl', n='pl'} end
if find(declNoun, 'in$') then return {'indefNom', n='pl'} end
if find(declNoun, 'ine$') then return {'indefConstr', n='pl'} end
if find(declNoun, 'ek$') then return {'indefNom', n='sg'} end
if find(declNoun, 'ekî$') then return {'indefConstr', 'indefObl', n='sg'} end
if find(declNoun, 'eke$') then return {'indefConstr', n='sg'} end
if find(declNoun, 'ekê$') then return {'indefObl', n='sg'} end
if find(declNoun, 'î$') then return {'defObl', n='sg'} end
if find(declNoun, 'a$') then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'nêr' then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê' then return {'defObl', 'voc', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê û nêr' then return {'defConstr', 'defObl', 'voc', n='sg'} end
if find(declNoun, 'o$') then return {'voc', n='sg'} end
end
-- long-form tags (confirm 'navkî' / vocative long form match your lang-data/ku exactly)
local caseTag = {
defConstr = 'îzafe', indefConstr = 'îzafe',
defObl = 'çemandî', indefObl = 'çemandî',
indefNom = 'navkî',
voc = 'baneşanî',
}
local defTag = {
defConstr = 'binavkirî', defObl = 'binavkirî', voc = 'binavkirî',
indefConstr = 'nebinavkirî', indefObl = 'nebinavkirî', indefNom = 'nebinavkirî',
}
local numTag = { sg = 'yekjimar', pl = 'pirjimar' }
local genTag = { ['mê'] = 'mê', ['nêr'] = 'nêr' }
local vowels = {a=true, e=true, ['ê']=true, i=true, ['î']=true, o=true, u=true, ['û']=true}
local function connectorAfter(word)
local last = mw.ustring.lower(mw.ustring.sub(word, -1))
return vowels[last] and 'ya' or 'a'
end
-- Interleave a connector after every tag in the row except the last
local function withConnectors(rowTags)
local out = {}
for i, tag in ipairs(rowTags) do
table.insert(out, tag)
if i < #rowTags then
table.insert(out, connectorAfter(tag))
end
end
return out
end
local function buildTags(case, gender, number)
if gender == 'mê û nêr' then
local out = {}
if caseTag[case] == 'îzafe' then
local row1 = withConnectors({ caseTag[case], defTag[case], numTag[number], 'nêr' })
for _, t in ipairs(row1) do table.insert(out, t) end
end
if caseTag[case] == 'çemandî' or caseTag[case] == 'baneşanî' then
local row2 = withConnectors({ caseTag[case], defTag[case], numTag[number], 'mê' })
table.insert(out, ';')
for _, t in ipairs(row2) do table.insert(out, t) end
end
return out
end
return withConnectors({ caseTag[case], defTag[case], numTag[number], genTag[gender] })
end
local function callInflectionOfT(lemma, allTags)
local parent_args = { [1] = 'ku', [2] = lemma, [3] = '' }
for i, tag in ipairs(allTags) do
parent_args[i + 3] = tag
end
local pseudoParent = mw.getCurrentFrame():newChild{ args = parent_args }
local pseudoChild = pseudoParent:newChild{ args = {} }
return m_form_of_templates.inflection_of_t(pseudoChild)
end
function export.formOfText(frame)
local args = frame:getParent().args
local noun = args[1]
local gender = args[2]
local declNoun = args['f'] or mw.title.getCurrentTitle().subpageText
local cases = export.caseName(declNoun, gender)
local allTags = {}
for i, case in ipairs(cases) do
local rowTags = buildTags(case, gender, cases.n)
for _, t in ipairs(rowTags) do table.insert(allTags, t) end
if i < #cases then table.insert(allTags, ';') end
end
return callInflectionOfT(noun, allTags)
end
return export
c6myvatcarc3xsh421fdwr6j5nw1u8j
6276950
6276949
2026-07-23T18:55:47Z
Ghybu
1393
Guhartoya [[Special:Diff/6276949|6276949]] yê [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|gotûbêj]]) şûnde kir
6276950
Scribunto
text/plain
local export = {}
local find = mw.ustring.find
local m_form_of_templates = require('Modul:form of/templates')
function export.caseName(declNoun, gender)
if find(declNoun, 'ên$') then return {'defConstr', n='pl'} end
if find(declNoun, 'inan$') then return {'indefObl', n='pl'} end
if find(declNoun, 'an$') then return {'defObl', n='pl'} end
if find(declNoun, 'in$') then return {'indefNom', n='pl'} end
if find(declNoun, 'ine$') then return {'indefConstr', n='pl'} end
if find(declNoun, 'ek$') then return {'indefNom', n='sg'} end
if find(declNoun, 'ekî$') then return {'indefConstr', 'indefObl', n='sg'} end
if find(declNoun, 'eke$') then return {'indefConstr', n='sg'} end
if find(declNoun, 'ekê$') then return {'indefObl', n='sg'} end
if find(declNoun, 'î$') then return {'defObl', n='sg'} end
if find(declNoun, 'a$') then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'nêr' then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê' then return {'defObl', 'voc', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê û nêr' then return {'defConstr', 'defObl', 'voc', n='sg'} end
if find(declNoun, 'o$') then return {'voc', n='sg'} end
end
-- long-form tags (confirm 'navkî' / vocative long form match your lang-data/ku exactly)
local caseTag = {
defConstr = 'îzafe', indefConstr = 'îzafe',
defObl = 'çemandî', indefObl = 'çemandî',
indefNom = 'navkî',
voc = 'baneşanî',
}
local defTag = {
defConstr = 'binavkirî', defObl = 'binavkirî', voc = 'binavkirî',
indefConstr = 'nebinavkirî', indefObl = 'nebinavkirî', indefNom = 'nebinavkirî',
}
local numTag = { sg = 'yekjimar', pl = 'pirjimar' }
local genTag = { ['mê'] = 'mê', ['nêr'] = 'nêr' }
local vowels = {a=true, e=true, ['ê']=true, i=true, ['î']=true, o=true, u=true, ['û']=true}
local function connectorAfter(word)
local last = mw.ustring.lower(mw.ustring.sub(word, -1))
return vowels[last] and 'ya' or 'a'
end
-- Interleave a connector after every tag in the row except the last
local function withConnectors(rowTags)
local out = {}
for i, tag in ipairs(rowTags) do
table.insert(out, tag)
if i < #rowTags then
table.insert(out, connectorAfter(tag))
end
end
return out
end
local function buildTags(case, gender, number)
if gender == 'mê û nêr' then
local row1 = withConnectors({ caseTag[case], defTag[case], numTag[number], 'mê' })
local row2 = withConnectors({ caseTag[case], defTag[case], numTag[number], 'nêr' })
local out = {}
for _, t in ipairs(row1) do table.insert(out, t) end
table.insert(out, ';')
for _, t in ipairs(row2) do table.insert(out, t) end
return out
end
return withConnectors({ caseTag[case], defTag[case], numTag[number], genTag[gender] })
end
local function callInflectionOfT(lemma, allTags)
local parent_args = { [1] = 'ku', [2] = lemma, [3] = '' }
for i, tag in ipairs(allTags) do
parent_args[i + 3] = tag
end
local pseudoParent = mw.getCurrentFrame():newChild{ args = parent_args }
local pseudoChild = pseudoParent:newChild{ args = {} }
return m_form_of_templates.inflection_of_t(pseudoChild)
end
function export.formOfText(frame)
local args = frame:getParent().args
local noun = args[1]
local gender = args[2]
local declNoun = args['f'] or mw.title.getCurrentTitle().subpageText
local cases = export.caseName(declNoun, gender)
local allTags = {}
for i, case in ipairs(cases) do
local rowTags = buildTags(case, gender, cases.n)
for _, t in ipairs(rowTags) do table.insert(allTags, t) end
if i < #cases then table.insert(allTags, ';') end
end
return callInflectionOfT(noun, allTags)
end
return export
1k7vn38mcf0vk1b2f9zt5y8whj22d6c
6276963
6276950
2026-07-23T19:30:12Z
Ghybu
1393
mê û nêr :)
6276963
Scribunto
text/plain
local export = {}
local find = mw.ustring.find
local m_form_of_templates = require('Modul:form of/templates')
function export.caseName(declNoun, gender)
if find(declNoun, 'ên$') then return {'defConstr', n='pl'} end
if find(declNoun, 'inan$') then return {'indefObl', n='pl'} end
if find(declNoun, 'an$') then return {'defObl', n='pl'} end
if find(declNoun, 'in$') then return {'indefNom', n='pl'} end
if find(declNoun, 'ine$') then return {'indefConstr', n='pl'} end
if find(declNoun, 'ek$') then return {'indefNom', n='sg'} end
if find(declNoun, 'ekî$') then return {'indefConstr', 'indefObl', n='sg', g='m'} end
if find(declNoun, 'eke$') then return {'indefConstr', n='sg', g='f'} end
if find(declNoun, 'ekê$') then return {'indefObl', n='sg', g='f'} end
if find(declNoun, 'î$') then return {'defObl', n='sg', g='m'} end
if find(declNoun, 'a$') then return {'defConstr', n='sg', g='f'} end
if find(declNoun, 'ê$') and gender == 'nêr' then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê' then return {'defObl', 'voc', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê û nêr' then return {'defConstr', 'defObl', 'voc', n='sg'} end
if find(declNoun, 'o$') then return {'voc', n='sg', g='m'} end
end
-- long-form tags (confirm 'navkî' / vocative long form match your lang-data/ku exactly)
local caseTag = {
defConstr = 'îzafe', indefConstr = 'îzafe',
defObl = 'çemandî', indefObl = 'çemandî',
indefNom = 'navkî',
voc = 'baneşanî',
}
local defTag = {
defConstr = 'binavkirî', defObl = 'binavkirî', voc = 'binavkirî',
indefConstr = 'nebinavkirî', indefObl = 'nebinavkirî', indefNom = 'nebinavkirî',
}
local numTag = { sg = 'yekjimar', pl = 'pirjimar' }
local genTag = { ['mê'] = 'mê', ['nêr'] = 'nêr' }
local vowels = {a=true, e=true, ['ê']=true, i=true, ['î']=true, o=true, u=true, ['û']=true}
local function connectorAfter(word)
local last = mw.ustring.lower(mw.ustring.sub(word, -1))
return vowels[last] and 'ya' or 'a'
end
-- Interleave a connector after every tag in the row except the last
local function withConnectors(rowTags)
local out = {}
for i, tag in ipairs(rowTags) do
table.insert(out, tag)
if i < #rowTags then
table.insert(out, connectorAfter(tag))
end
end
return out
end
local function buildTags(case, gender, number, g, cases)
if gender == 'mê û nêr' and #cases > 1 then
if caseTag[case] == 'îzafe' then
gender = 'nêr'
end
if caseTag[case] == 'çemandî' or caseTag[case] == 'baneşanî' then
gender = 'mê'
end
elseif gender == 'mê û nêr' and g then
if g == 'f' then gender = 'mê' end
if g == 'm' then gender = 'nêr' end
end
return withConnectors({ caseTag[case], defTag[case], numTag[number], genTag[gender] })
end
local function callInflectionOfT(lemma, allTags)
local parent_args = { [1] = 'ku', [2] = lemma, [3] = '' }
for i, tag in ipairs(allTags) do
parent_args[i + 3] = tag
end
local pseudoParent = mw.getCurrentFrame():newChild{ args = parent_args }
local pseudoChild = pseudoParent:newChild{ args = {} }
return m_form_of_templates.inflection_of_t(pseudoChild)
end
function export.formOfText(frame)
local args = frame:getParent().args
local noun = args[1]
local gender = args[2]
local declNoun = args['f'] or mw.title.getCurrentTitle().subpageText
local cases = export.caseName(declNoun, gender)
local allTags = {}
for i, case in ipairs(cases) do
local rowTags = buildTags(case, gender, cases.n, cases.g, cases)
for _, t in ipairs(rowTags) do table.insert(allTags, t) end
if i < #cases then table.insert(allTags, ';') end
end
return callInflectionOfT(noun, allTags)
end
return export
cuwcblehkuqbumw5gn8r54mmy7pzdrw
6277059
6276963
2026-07-24T03:01:05Z
Ghybu
1393
6277059
Scribunto
text/plain
local export = {}
local find = mw.ustring.find
local m_form_of_templates = require('Modul:form of/templates')
function export.caseName(declNoun, gender)
if find(declNoun, 'ên$') then return {'defConstr', n='pl'} end
if find(declNoun, 'inan$') then return {'indefObl', n='pl'} end
if find(declNoun, 'an$') then return {'defObl', n='pl'} end
if find(declNoun, 'in$') then return {'indefNom', n='pl'} end
if find(declNoun, 'ine$') then return {'indefConstr', n='pl'} end
if find(declNoun, 'ek$') then return {'indefNom', n='sg'} end
if find(declNoun, 'ekî$') then return {'indefConstr', 'indefObl', n='sg', g='m'} end
if find(declNoun, 'eke$') then return {'indefConstr', n='sg', g='f'} end
if find(declNoun, 'ekê$') then return {'indefObl', n='sg', g='f'} end
if find(declNoun, 'î$') then return {'defObl', n='sg', g='m'} end
if find(declNoun, 'a$') then return {'defConstr', n='sg', g='f'} end
if find(declNoun, 'ê$') and gender == 'nêr' then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê' then return {'defObl', 'voc', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê û nêr' then return {'defConstr', 'defObl', 'voc', n='sg'} end
if find(declNoun, 'o$') then return {'voc', n='sg', g='m'} end
end
-- long-form tags (confirm 'navkî' / vocative long form match your lang-data/ku exactly)
local caseTag = {
defConstr = 'îzafe', indefConstr = 'îzafe',
defObl = 'çemandî', indefObl = 'çemandî',
indefNom = 'navkî',
voc = 'baneşanî',
}
local defTag = {
defConstr = 'binavkirî', defObl = 'binavkirî', voc = 'binavkirî',
indefConstr = 'nebinavkirî', indefObl = 'nebinavkirî', indefNom = 'nebinavkirî',
}
local numTag = { sg = 'yekjimar', pl = 'pirjimar' }
local genTag = { ['mê'] = 'mê', ['nêr'] = 'nêr' }
local vowels = {a=true, e=true, ['ê']=true, i=true, ['î']=true, o=true, u=true, ['û']=true}
local function connectorAfter(word)
local last = mw.ustring.lower(mw.ustring.sub(word, -1))
return vowels[last] and 'ya' or 'a'
end
-- Interleave a connector after every tag in the row except the last
local function withConnectors(rowTags)
local out = {}
for i, tag in ipairs(rowTags) do
table.insert(out, tag)
if i < #rowTags then
table.insert(out, connectorAfter(tag))
end
end
return out
end
local function buildTags(case, gender, number, g, cases)
if gender == 'mê û nêr' and #cases > 2 then
if caseTag[case] == 'îzafe' then
gender = 'nêr'
end
if caseTag[case] == 'çemandî' or caseTag[case] == 'baneşanî' then
gender = 'mê'
end
elseif gender == 'mê û nêr' and g then
if g == 'f' then gender = 'mê' end
if g == 'm' then gender = 'nêr' end
end
return withConnectors({ caseTag[case], defTag[case], numTag[number], genTag[gender] })
end
local function callInflectionOfT(lemma, allTags)
local parent_args = { [1] = 'ku', [2] = lemma, [3] = '' }
for i, tag in ipairs(allTags) do
parent_args[i + 3] = tag
end
local pseudoParent = mw.getCurrentFrame():newChild{ args = parent_args }
local pseudoChild = pseudoParent:newChild{ args = {} }
return m_form_of_templates.inflection_of_t(pseudoChild)
end
function export.formOfText(frame)
local args = frame:getParent().args
local noun = args[1]
local gender = args[2]
local declNoun = args['f'] or mw.title.getCurrentTitle().subpageText
local cases = export.caseName(declNoun, gender)
local allTags = {}
for i, case in ipairs(cases) do
local rowTags = buildTags(case, gender, cases.n, cases.g, cases)
for _, t in ipairs(rowTags) do table.insert(allTags, t) end
if i < #cases then table.insert(allTags, ';') end
end
return callInflectionOfT(noun, allTags)
end
return export
g6x96s1cbq7i7xokgu6za0e6crgw5qs
6277060
6277059
2026-07-24T03:09:37Z
Ghybu
1393
6277060
Scribunto
text/plain
local export = {}
local find = mw.ustring.find
local m_form_of_templates = require('Modul:form of/templates')
function export.caseName(declNoun, gender)
if find(declNoun, 'ên$') then return {'defConstr', n='pl'} end
if find(declNoun, 'inan$') then return {'indefObl', n='pl'} end
if find(declNoun, 'an$') then return {'defObl', n='pl'} end
if find(declNoun, 'in$') then return {'indefNom', n='pl'} end
if find(declNoun, 'ine$') then return {'indefConstr', n='pl'} end
if find(declNoun, 'ek$') then return {'indefNom', n='sg'} end
if find(declNoun, 'ekî$') then return {'indefConstr', 'indefObl', n='sg', g='m'} end
if find(declNoun, 'eke$') then return {'indefConstr', n='sg', g='f'} end
if find(declNoun, 'ekê$') then return {'indefObl', n='sg', g='f'} end
if find(declNoun, 'î$') then return {'defObl', n='sg', g='m'} end
if find(declNoun, 'a$') then return {'defConstr', n='sg', g='f'} end
if find(declNoun, 'ê$') and gender == 'nêr' then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê' then return {'defObl', 'voc', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê û nêr' then return {'defConstr', 'defObl', 'voc', n='sg'} end
if find(declNoun, 'o$') then return {'voc', n='sg', g='m'} end
end
-- long-form tags (confirm 'navkî' / vocative long form match your lang-data/ku exactly)
local caseTag = {
defConstr = 'îzafe', indefConstr = 'îzafe',
defObl = 'çemandî', indefObl = 'çemandî',
indefNom = 'navkî',
voc = 'baneşanî',
}
local defTag = {
defConstr = 'binavkirî', defObl = 'binavkirî', voc = 'binavkirî',
indefConstr = 'nebinavkirî', indefObl = 'nebinavkirî', indefNom = 'nebinavkirî',
}
local numTag = { sg = 'yekjimar', pl = 'pirjimar' }
local genTag = { ['mê'] = 'mê', ['nêr'] = 'nêr' }
local vowels = {a=true, e=true, ['ê']=true, i=true, ['î']=true, o=true, u=true, ['û']=true}
local function connectorAfter(word)
local last = mw.ustring.lower(mw.ustring.sub(word, -1))
return vowels[last] and 'ya' or 'a'
end
-- Interleave a connector after every tag in the row except the last
local function withConnectors(rowTags)
local out = {}
for i, tag in ipairs(rowTags) do
table.insert(out, tag)
if i < #rowTags then
table.insert(out, connectorAfter(tag))
end
end
return out
end
local function buildTags(case, gender, cases)
if gender == 'mê û nêr' and #cases > 2 then
if caseTag[case] == 'îzafe' then
gender = 'nêr'
end
if caseTag[case] == 'çemandî' or caseTag[case] == 'baneşanî' then
gender = 'mê'
end
elseif gender == 'mê û nêr' and cases.g then
if cases.g == 'f' then gender = 'mê' end
if cases.g == 'm' then gender = 'nêr' end
end
return withConnectors({ caseTag[case], defTag[case], numTag[cases.n], genTag[gender] })
end
local function callInflectionOfT(lemma, allTags)
local parent_args = { [1] = 'ku', [2] = lemma, [3] = '' }
for i, tag in ipairs(allTags) do
parent_args[i + 3] = tag
end
local pseudoParent = mw.getCurrentFrame():newChild{ args = parent_args }
local pseudoChild = pseudoParent:newChild{ args = {} }
return m_form_of_templates.inflection_of_t(pseudoChild)
end
function export.formOfText(frame)
local args = frame:getParent().args
local noun = args[1]
local gender = args[2]
local declNoun = args['f'] or mw.title.getCurrentTitle().subpageText
local cases = export.caseName(declNoun, gender)
local allTags = {}
for i, case in ipairs(cases) do
local rowTags = buildTags(case, gender, cases)
for _, t in ipairs(rowTags) do table.insert(allTags, t) end
if i < #cases then table.insert(allTags, ';') end
end
return callInflectionOfT(noun, allTags)
end
return export
ezp1fe6u6s39jdzptbwpt5cqum15qu0
6277061
6277060
2026-07-24T03:13:49Z
Ghybu
1393
6277061
Scribunto
text/plain
local export = {}
local find = mw.ustring.find
local m_form_of_templates = require('Modul:form of/templates')
function export.caseName(declNoun, gender)
if find(declNoun, 'ên$') then return {'defConstr', n='pl'} end
if find(declNoun, 'inan$') then return {'indefObl', n='pl'} end
if find(declNoun, 'an$') then return {'defObl', n='pl'} end
if find(declNoun, 'in$') then return {'indefNom', n='pl'} end
if find(declNoun, 'ine$') then return {'indefConstr', n='pl'} end
if find(declNoun, 'ek$') then return {'indefNom', n='sg'} end
if find(declNoun, 'ekî$') then return {'indefConstr', 'indefObl', n='sg', g='m'} end
if find(declNoun, 'eke$') then return {'indefConstr', n='sg', g='f'} end
if find(declNoun, 'ekê$') then return {'indefObl', n='sg', g='f'} end
if find(declNoun, 'î$') then return {'defObl', n='sg', g='m'} end
if find(declNoun, 'a$') then return {'defConstr', n='sg', g='f'} end
if find(declNoun, 'ê$') and gender == 'nêr' then return {'defConstr', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê' then return {'defObl', 'voc', n='sg'} end
if find(declNoun, 'ê$') and gender == 'mê û nêr' then return {'defConstr', 'defObl', 'voc', n='sg'} end
if find(declNoun, 'ino$') then return {'voc', n='pl'} end
if find(declNoun, 'o$') then return {'voc', n='sg', g='m'} end
end
-- long-form tags (confirm 'navkî' / vocative long form match your lang-data/ku exactly)
local caseTag = {
defConstr = 'îzafe', indefConstr = 'îzafe',
defObl = 'çemandî', indefObl = 'çemandî',
indefNom = 'navkî',
voc = 'baneşanî',
}
local defTag = {
defConstr = 'binavkirî', defObl = 'binavkirî', voc = 'binavkirî',
indefConstr = 'nebinavkirî', indefObl = 'nebinavkirî', indefNom = 'nebinavkirî',
}
local numTag = { sg = 'yekjimar', pl = 'pirjimar' }
local genTag = { ['mê'] = 'mê', ['nêr'] = 'nêr' }
local vowels = {a=true, e=true, ['ê']=true, i=true, ['î']=true, o=true, u=true, ['û']=true}
local function connectorAfter(word)
local last = mw.ustring.lower(mw.ustring.sub(word, -1))
return vowels[last] and 'ya' or 'a'
end
-- Interleave a connector after every tag in the row except the last
local function withConnectors(rowTags)
local out = {}
for i, tag in ipairs(rowTags) do
table.insert(out, tag)
if i < #rowTags then
table.insert(out, connectorAfter(tag))
end
end
return out
end
local function buildTags(case, gender, cases)
if gender == 'mê û nêr' and #cases > 2 then
if caseTag[case] == 'îzafe' then
gender = 'nêr'
end
if caseTag[case] == 'çemandî' or caseTag[case] == 'baneşanî' then
gender = 'mê'
end
elseif gender == 'mê û nêr' and cases.g then
if cases.g == 'f' then gender = 'mê' end
if cases.g == 'm' then gender = 'nêr' end
end
return withConnectors({ caseTag[case], defTag[case], numTag[cases.n], genTag[gender] })
end
local function callInflectionOfT(lemma, allTags)
local parent_args = { [1] = 'ku', [2] = lemma, [3] = '' }
for i, tag in ipairs(allTags) do
parent_args[i + 3] = tag
end
local pseudoParent = mw.getCurrentFrame():newChild{ args = parent_args }
local pseudoChild = pseudoParent:newChild{ args = {} }
return m_form_of_templates.inflection_of_t(pseudoChild)
end
function export.formOfText(frame)
local args = frame:getParent().args
local noun = args[1]
local gender = args[2]
local declNoun = args['f'] or mw.title.getCurrentTitle().subpageText
local cases = export.caseName(declNoun, gender)
local allTags = {}
for i, case in ipairs(cases) do
local rowTags = buildTags(case, gender, cases)
for _, t in ipairs(rowTags) do table.insert(allTags, t) end
if i < #cases then table.insert(allTags, ';') end
end
return callInflectionOfT(noun, allTags)
end
return export
g30ykn6n0p4armk5d58vpja303yplc0
Şablon:jêder-kitêb/ceribandin
10
1358142
6276957
6276906
2026-07-23T19:20:52Z
Wikihez
7344
6276957
wikitext
text/x-wiki
{{#invoke:ceribandin/Wikihez|kiteb_t}}<noinclude><!--
-->{{documentation}}</noinclude>
6gi3nkzkxh3ddd3dx7tkre0tcif8t14
6277030
6276957
2026-07-24T01:31:07Z
Wikihez
7344
6277030
wikitext
text/x-wiki
<includeonly>{{#invoke:quote|quote_t|type=book}}</includeonly><!--
--><noinclude>{{quote-book|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}<!--
-->{{documentation}}</noinclude>
pcy9j7t6zzr2fahebvfk3pwkaa4lxuc
6277031
6277030
2026-07-24T01:31:21Z
Wikihez
7344
6277031
wikitext
text/x-wiki
<includeonly>{{#invoke:quote|quote_t|type=book}}</includeonly><!--
--><noinclude>{{jêder-kitêb/ceribandin|en|author=F[rancis] Scott Fitzgerald|authorlink=F. Scott Fitzgerald|chapter=IX|title=[[w:The Great Gatsby|The Great Gatsby]]|location=New York, N.Y.|publisher=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|year=1925|oclc=884653065|location2=New York, N.Y.|publisher2=Charles Scribner’s Sons|year2=1953|isbn2=978-0-684-16498-4|page2=182|pageurl2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|passage=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}}<!--
-->{{documentation}}</noinclude>
e1s0p8ymtzxrxbiutxizv38unip278n
Kategorî:Rêzimanî bi osmanî
14
1358149
6276930
2026-07-23T12:35:14Z
Wikihez
7344
Bi riya [[Bikarhêner:Wikihez/skrîpt/js/addAutoCat.js/belge|JS]]: {{katbike}} û înterwîkî
6276930
wikitext
text/x-wiki
{{katbike}}
1dpx16ue3vl1l6c1hkqu0q6ftpl2ezh
Gotûbêja bikarhêner:Peyvkar
3
1358150
6276951
2026-07-23T19:01:45Z
Wikihez
7344
xêrhatin lê zêde kir
6276951
wikitext
text/x-wiki
{{bixêrhatin}}
== Xêrhatin ==
Merheba bikarhênera/ê me ya/ê hêja: ''Peyvkar'',
Bi xêr hatî ferhenga kurdî-kurdî WÎKÎFERHENGê {{=)}}
Em hêvîdar in ku tu jî di avakirina vê ferhengê de alîkariya me bikî.
Eger pirsên te hebin, dikarî li ser [[WF:Dîwan]]ê bipirsî.
Xebatxweş.
:—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:01, 23 tîrmeh 2026 (UTC)
762h3wst7lhs396bh1aqe7dscnx17ri
6277064
6276951
2026-07-24T04:56:59Z
Peyvkar
14865
/* Xêrhatin */ Bersiv
6277064
wikitext
text/x-wiki
{{bixêrhatin}}
== Xêrhatin ==
Merheba bikarhênera/ê me ya/ê hêja: ''Peyvkar'',
Bi xêr hatî ferhenga kurdî-kurdî WÎKÎFERHENGê {{=)}}
Em hêvîdar in ku tu jî di avakirina vê ferhengê de alîkariya me bikî.
Eger pirsên te hebin, dikarî li ser [[WF:Dîwan]]ê bipirsî.
Xebatxweş.
:—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:01, 23 tîrmeh 2026 (UTC)
:Di nav xêrê da bin. Malîava, her hebin. [[Bikarhêner:Peyvkar|Peyvkar]] ([[Gotûbêja bikarhêner:Peyvkar#top|gotûbêj]]) 04:56, 24 tîrmeh 2026 (UTC)
igltt1q9v0ua266zvjdey1b9u063x16
6277072
6277064
2026-07-24T08:08:50Z
Wikihez
7344
/* Xêrhatin */ Bersiv
6277072
wikitext
text/x-wiki
{{bixêrhatin}}
== Xêrhatin ==
Merheba bikarhênera/ê me ya/ê hêja: ''Peyvkar'',
Bi xêr hatî ferhenga kurdî-kurdî WÎKÎFERHENGê {{=)}}
Em hêvîdar in ku tu jî di avakirina vê ferhengê de alîkariya me bikî.
Eger pirsên te hebin, dikarî li ser [[WF:Dîwan]]ê bipirsî.
Xebatxweş.
:—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:01, 23 tîrmeh 2026 (UTC)
:Di nav xêrê da bin. Malîava, her hebin. [[Bikarhêner:Peyvkar|Peyvkar]] ([[Gotûbêja bikarhêner:Peyvkar#top|gotûbêj]]) 04:56, 24 tîrmeh 2026 (UTC)
::Dixwazim hinek bala te bikşînim ser çend meseleyan. Te çend kelîme [https://ku.wiktionary.org/w/index.php?title=yer&diff=prev&oldid=6272107] [https://ku.wiktionary.org/w/index.php?title=sivilce&diff=prev&oldid=6276346], [https://ku.wiktionary.org/w/index.php?title=g%C3%BCzelce&diff=prev&oldid=6276947], [https://ku.wiktionary.org/w/index.php?title=telefon_etmek&diff=prev&oldid=6277063] zêde kir bêyî ti çavkanî bidî, eger tu yê li vir daîmî bî, lazim e usûlên dayîna çavkaniyan [[Alîkarî:Çavkanî]] û krîterên tevlîkirinê [[Wîkîferheng:Pîvekên tevlîkirinê]] hîn bibî. Qewet be! [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:08, 24 tîrmeh 2026 (UTC)
qcvy78bw0kuhll8395u87cgxnuqygum
Modul:ceribandin/Wikihez
828
1358151
6276956
2026-07-23T19:20:02Z
Wikihez
7344
Rûpel bi "--[=[ This module unifies the old {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}} and {{jêder-malper}} templates (and the helper templates {{pirtûk}}, {{kovar}}, {{malper}} that used to feed them via |2=) into a single citation system. The parameter names are kept identical to the old templates (including the ones with spaces in them, e.g. |sernavê orjînal=, |sala weşandinê=) so that existing entries using these templates keep working without any wikit..." hat çêkirin
6276956
Scribunto
text/plain
--[=[
This module unifies the old {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}} and {{jêder-malper}} templates
(and the helper templates {{pirtûk}}, {{kovar}}, {{malper}} that used to feed them via |2=) into a
single citation system.
The parameter names are kept identical to the old templates (including the ones with spaces in them,
e.g. |sernavê orjînal=, |sala weşandinê=) so that existing entries using these templates keep working
without any wikitext changes; only the template bodies need to be swapped for #invoke calls (see the
bottom of this file for the exact template code to use).
The quoted-text portion (|jêgirtin=/|jg=, |tr=, |werger=, etc.) is delegated to [[Module:usex/ceribandin]],
the same module [[Module:quote]] uses, instead of the ad hoc {{zim}}/{{werger?}} wikitext logic in the old
{{jêder}} template. This automatically gives us proper script tagging, transliteration fallback and
translation-request categorization ("Daxwazên tercimekirina...") for free, since that module already
implements the "don't ask for a translation if the language is ku/navz/und" rule that {{jêder}} had to
hand-roll.
The hidden <bdi class="example">...</bdi> marker used by [[MediaWiki:Gadget-wikt.add-examples]] to find
insertion points is preserved exactly, since removing it would break that gadget.
]=]
local export = {}
------------------------------------------------------------------
-- Module names (these already exist on ku.wiktionary; see [[Module:quote]] and [[Module:usex/ceribandin]]
-- for the same require pattern)
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
-- Lazy-load heavy modules only if actually needed, same pattern used throughout [[Module:quote]].
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function track(page)
debug_track("jêder/" .. page)
end
------------------------------------------------------------------
-- Generic helpers
------------------------------------------------------------------
-- if-not-empty: trims strings, converts "" to nil, passes everything else through.
local function ine(v)
if v == nil then
return nil
end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then
return nil
end
end
return v
end
-- Fetch the first non-empty value out of several possible parameter names. This replaces the
-- {{{a|{{{b|}}}}}} alias chains scattered across the old wikitext templates with a single call, e.g.
-- first(args, "werger", "w", "tercime", "t").
local function first(args, ...)
for _, name in ipairs{...} do
local v = ine(args[name])
if v then
return v, name
end
end
return nil
end
-- Mirrors the old {{j-pêwîst}} template: flags a citation field as "supplied but easy to forget",
-- e.g. author/year, so it can still be styled separately via CSS if wanted.
local function j_pewist(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local function err(text)
return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>"
end
-- Collect a numbered run of parameters (nivîskar, nivîskar2, nivîskar3, ...) into a plain list.
-- Unlike the old {{pirtûk}} template (which only ever looked at nivîskar/nivîskar2/nivîskar3, silently
-- ignoring nivîskar4/nivîskar5 even though {{jêder-kitêb}} accepted them), this keeps going for as long
-- as consecutive numbered parameters are supplied.
local function collect_numbered(args, base)
local list = {}
local v = ine(args[base])
if not v then
return list
end
insert(list, v)
local i = 2
while true do
v = ine(args[base .. i])
if not v then
break
end
insert(list, v)
i = i + 1
end
return list
end
-- Join a list with commas and a final "û" ("and"), e.g. {"Ehmed", "Berivan", "Rojin"} becomes
-- "Ehmed, Berivan û Rojin".
local function join_and(list)
if #list == 0 then
return nil
elseif #list == 1 then
return list[1]
elseif #list == 2 then
return list[1] .. " û " .. list[2]
else
return concat(list, ", ", 1, #list - 1) .. " û " .. list[#list]
end
end
-- Hidden marker expected by [[MediaWiki:Gadget-wikt.add-examples]] to locate this citation in the
-- rendered page; do NOT remove without updating that gadget too.
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
-- Resolve a language code to a language object, falling back to a tracking category (reusing the same
-- category the old {{jêder}} template used for a missing/bad language or missing second parameter) if it
-- can't be resolved.
local function get_langobj(langcode, categories)
if not langcode then
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then
return lang
end
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
------------------------------------------------------------------
-- Quoted-text portion (replaces {{jêder}}'s own {{zim}}/{{werger?}} logic)
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local jegirtin = first(args, "jêgirtin", "jg")
if not jegirtin then
return nil
end
local sc = first(args, "sc")
sc = sc and get_script(sc) or nil
local tr = first(args, "tr")
local werger = first(args, "werger", "w", "tercime", "t")
local nekat = ine(args.nekat)
local formatted = format_usex{
lang = lang,
termlang = lang,
usex = jegirtin,
sc = sc,
transliteration = tr,
translation = werger,
quote = "quote-meta",
inline = true,
nocat = nekat,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Type-specific source-line formatters (replace {{pirtûk}}, {{kovar}}, {{malper}})
------------------------------------------------------------------
local function format_year(args)
local sal = first(args, "sal", "dîrok", "tarîx")
if not sal then
return nil
end
return j_pewist("'''" .. sal .. "'''")
end
local function format_kiteb(args, categories)
local parts = {}
local year = format_year(args)
if year then
insert(parts, year)
end
local authors = collect_numbered(args, "nivîskar")
local amadekar = first(args, "amadekar")
local werger_val = first(args, "wergêr", "terciman")
if #authors > 0 then
insert(parts, j_pewist(join_and(authors)))
end
if amadekar then
insert(parts, j_pewist("Amadekar: " .. amadekar))
end
if werger_val then
insert(parts, "(" .. j_pewist("terciman: " .. werger_val) .. ")")
end
local sernav = first(args, "sernav")
local url = first(args, "url")
if sernav then
local title = "''" .. sernav .. "''"
if url then
title = title .. "<sup>[" .. url .. "]</sup>"
end
insert(parts, title)
else
insert(parts, err("sernavê pirtûkê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
local sernav_orjinal = first(args, "sernavê orjînal")
if sernav_orjinal then
insert(parts, "[\"" .. sernav_orjinal .. "\"]")
end
local wergera_sernav = first(args, "tercimeya sernav", "wergera sernav")
if wergera_sernav then
insert(parts, "[\"" .. wergera_sernav .. "\"]")
end
local wesanxane = first(args, "weşanxane")
if wesanxane then
insert(parts, wesanxane)
end
local cih = first(args, "cih")
if cih then
insert(parts, cih)
end
local sala_wesandine = first(args, "sala weşandinê")
if sala_wesandine then
insert(parts, "sala weşandinê: " .. sala_wesandine)
end
local cap_val = first(args, "çap")
if cap_val then
insert(parts, "çapa " .. cap_val .. (cap_val == "1" and "ê" or "an"))
end
local cild = first(args, "cild")
if cild then
insert(parts, "cild " .. cild)
end
local rupel_val = first(args, "rûpel", "r")
if rupel_val then
insert(parts, "r. " .. rupel_val)
end
local isbn = first(args, "isbn")
if isbn then
insert(parts, "ISBN " .. isbn)
end
return concat(parts, ", ")
end
local function format_kovar(args, categories)
local parts = {}
local year = format_year(args)
if year then
insert(parts, year)
end
local authors = collect_numbered(args, "nivîskar")
local amadekar = first(args, "amadekar")
local werger_val = first(args, "wergêr")
if #authors > 0 then
insert(parts, j_pewist(join_and(authors)))
end
if amadekar then
insert(parts, j_pewist("Amadekar: " .. amadekar))
end
if werger_val then
insert(parts, "(" .. j_pewist("terciman: " .. werger_val) .. ")")
end
local sernav = first(args, "sernav")
local url = first(args, "url")
if sernav then
local title = "“" .. sernav .. "”"
if url then
title = title .. "<sup>[" .. url .. "]</sup>"
end
insert(parts, title)
end
local sernav_orjinal = first(args, "sernavê orjînal")
if sernav_orjinal then
insert(parts, "[\"" .. sernav_orjinal .. "\"]")
end
local kovar = first(args, "kovar")
if kovar then
insert(parts, "''" .. kovar .. "''")
else
insert(parts, err("navê kovarê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
local wesanxane = first(args, "weşanxane")
if wesanxane then
insert(parts, wesanxane)
end
local cild_val = first(args, "cîld")
if cild_val then
insert(parts, "cîld " .. cild_val)
end
local hejmar = first(args, "hejmar", "hj")
if hejmar then
insert(parts, "hejmar " .. hejmar)
end
local sala_wesandine = first(args, "sala weşandinê")
if sala_wesandine then
insert(parts, "sala weşandinê: " .. sala_wesandine)
end
local rupel_val = first(args, "rûpel", "r")
if rupel_val then
insert(parts, "r. " .. rupel_val)
end
return concat(parts, ", ")
end
local function format_malper(args, categories)
local parts = {}
local nivIskar_val = first(args, "nivîskar")
local werger_val = first(args, "terciman", "wergêr")
if nivIskar_val then
insert(parts, j_pewist(nivIskar_val))
end
if werger_val then
insert(parts, "(" .. j_pewist("terciman: " .. werger_val) .. ")")
end
local sernav = first(args, "sernav")
local url = first(args, "url")
if sernav then
local title = "“" .. sernav .. "”"
if url then
title = title .. "<sup>[" .. url .. "]</sup>"
end
insert(parts, title)
end
local malper = first(args, "malper", "websîte")
if malper then
insert(parts, malper)
else
insert(parts, err("navê malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
if url then
local arsiv_val = first(args, "arşîv")
local link_text = " →<span class=\"plainlinks\"> [" .. url .. " lînk]</span>"
if arsiv_val then
link_text = link_text .. " [<span class=\"plainlinks\">[" .. arsiv_val .. " arşîv]</span>]"
else
link_text = link_text .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
insert(parts, link_text)
else
insert(parts, err("lînka malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
local sal = format_year(args)
if sal then
insert(parts, sal)
end
return concat(parts, ", ")
end
local format_by_kind = {
["kitêb"] = format_kiteb,
["kovar"] = format_kovar,
["malper"] = format_malper,
}
local kind_category_word = {
["kitêb"] = "kitêban",
["kovar"] = "kovaran",
["malper"] = "malperan",
}
------------------------------------------------------------------
-- Shared rendering logic
------------------------------------------------------------------
--[=[
Render a full citation.
* `args` is the raw table of template arguments (frame:getParent().args is fine as-is; values are
normalized with ine() internally as needed).
* `kind` is one of "kitêb", "kovar", "malper", or nil for a bare {{jêder}} call, in which case |2= is
expected to already be fully-formatted wikitext (kept only for backward compatibility with any page
that still calls {{jêder}} directly with a hand-built |2=; new citations should use |kind=).
]=]
function export.render(args, kind)
local categories = {}
local langcode = ine(args[1])
local lang = get_langobj(langcode, categories)
local source_text
if kind then
source_text = format_by_kind[kind](args, categories)
else
source_text = ine(args[2])
if not source_text then
source_text = err("divê parametreya duyemîn (çavkanî) were dayîn")
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
end
local quote_text = build_quote(args, lang, langcode) or ""
-- {{jêder-malper}}/format_malper already builds its own URL/archive link inline, since a website
-- citation's whole point is the link; for the other two kinds we add it here, exactly like the old
-- {{jêder}} template did.
local link_text = ""
if kind ~= "malper" then
local url = first(args, "url")
if url then
local arsiv_val = first(args, "arşîv")
link_text = " →<span class=\"plainlinks\"> [" .. url .. " here lînkê]</span>"
if arsiv_val then
link_text = link_text .. " [<span class=\"plainlinks\">[" .. arsiv_val .. " arşîv]</span>]"
else
link_text = link_text .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
end
end
local nekat = ine(args.nekat)
local cat_text = ""
if not nekat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
if kind then
insert(cats, "Jêgirtinên " .. kind_category_word[kind] .. " bi " .. langname)
end
end
for _, c in ipairs(categories) do
insert(cats, c)
end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. link_text .. ")</span>"
.. cat_text
end
------------------------------------------------------------------
-- Template entry points
------------------------------------------------------------------
-- {{jêder}}: kept for backward compatibility (expects a hand-built |2=).
function export.jeder_t(frame)
return export.render(frame:getParent().args, nil)
end
-- {{jêder-kitêb}}
function export.kiteb_t(frame)
return export.render(frame:getParent().args, "kitêb")
end
-- {{jêder-kovar}}
function export.kovar_t(frame)
return export.render(frame:getParent().args, "kovar")
end
-- {{jêder-malper}}
function export.malper_t(frame)
return export.render(frame:getParent().args, "malper")
end
return export
fqx721jfp00e6kz1zq7hm0gdyi3u561
6276971
6276956
2026-07-23T20:37:04Z
Wikihez
7344
6276971
Scribunto
text/plain
--[=[
This module unifies the old {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}} and {{jêder-malper}} templates
(and the helper templates {{pirtûk}}, {{kovar}}, {{malper}} that used to feed them via |2=) into a
single citation system.
The parameter names are kept identical to the old templates (including the ones with spaces in them,
e.g. |sernavê orjînal=, |sala weşandinê=) so that existing entries using these templates keep working
without any wikitext changes; only the template bodies need to be swapped for #invoke calls (see the
bottom of this file for the exact template code to use).
Argument handling now goes through [[Module:parameters]] (the same module [[Module:quote]] uses),
instead of the hand-rolled first()/ine()/collect_numbered() helpers the previous version had. All the
accepted parameters — including their aliases and the numbered nivîskar/nivîskar2/... run — are now
declared once in the `params` table below, so ADDING A NEW PARAMETER is a one-line change there:
* a plain field ....... foo = true
* an alias ............ foo = {alias_of = "bar"}
* a numbered run ...... foo = {list = true} -> foo, foo2, foo3, ... collected into args.foo
Module:parameters trims the values and drops empty ones for us, so no more ine() calls are needed, and
it resolves aliases for us, so no more first() chains are needed.
The quoted-text portion (|jêgirtin=/|jg=, |tr=, |werger=, etc.) is delegated to [[Module:usex/ceribandin]],
the same module [[Module:quote]] uses, instead of the ad hoc {{zim}}/{{werger?}} wikitext logic in the old
{{jêder}} template. This automatically gives us proper script tagging, transliteration fallback and
translation-request categorization ("Daxwazên tercimekirina...") for free, since that module already
implements the "don't ask for a translation if the language is ku/navz/und" rule that {{jêder}} had to
hand-roll.
The hidden <bdi class="example">...</bdi> marker used by [[MediaWiki:Gadget-wikt.add-examples]] to find
insertion points is preserved exactly, since removing it would break that gadget.
]=]
local export = {}
------------------------------------------------------------------
-- Module names (these already exist on ku.wiktionary; see [[Module:quote]] and [[Module:usex/ceribandin]]
-- for the same require pattern)
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
-- Lazy-load heavy modules only if actually needed, same pattern used throughout [[Module:quote]].
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function track(page)
debug_track("jêder/" .. page)
end
------------------------------------------------------------------
-- Parameter specification (single source of truth — add new parameters here)
------------------------------------------------------------------
-- Every parameter any of {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}} accepts is
-- declared here. Because Module:parameters only complains about *supplied* parameters it doesn't know,
-- one combined table is safe for all four templates: a parameter that a given template never uses simply
-- stays nil. `process` below is called with return_unknown = true, so a stray/legacy parameter on an
-- existing page is ignored rather than turned into a red error (matching the old module's behaviour).
local params = {
[1] = true, -- language code (resolved manually, see get_langobj)
[2] = true, -- pre-built source text, for bare {{jêder}} only
-- quoted-text portion
["jêgirtin"] = true,
["jg"] = {alias_of = "jêgirtin"},
["sc"] = true,
["tr"] = true,
["werger"] = true,
["w"] = {alias_of = "werger"},
["tercime"] = {alias_of = "werger"},
["t"] = {alias_of = "werger"},
["nekat"] = true,
-- people
["nivîskar"] = {list = true}, -- nivîskar, nivîskar2, nivîskar3, ...
["amadekar"] = true,
["wergêr"] = true, -- translator
["terciman"] = {alias_of = "wergêr"},
-- title
["sernav"] = true,
["sernavê orjînal"] = true,
["tercimeya sernav"] = true,
["wergera sernav"] = {alias_of = "tercimeya sernav"},
-- date / year
["sal"] = true,
["dîrok"] = {alias_of = "sal"},
["tarîx"] = {alias_of = "sal"},
["sala weşandinê"] = true,
-- publication details
["weşanxane"] = true,
["cih"] = true,
["çap"] = true,
["cild"] = true, -- volume (kitêb spelling)
["cîld"] = true, -- volume (kovar spelling — kept separate on purpose)
["kovar"] = true,
["hejmar"] = true,
["hj"] = {alias_of = "hejmar"},
["rûpel"] = true,
["r"] = {alias_of = "rûpel"},
["isbn"] = true,
-- website
["malper"] = true,
["websîte"] = {alias_of = "malper"},
["url"] = true,
["arşîv"] = true,
}
------------------------------------------------------------------
-- Generic helpers
------------------------------------------------------------------
-- Mirrors the old {{j-pêwîst}} template: flags a citation field as "supplied but easy to forget",
-- e.g. author/year, so it can still be styled separately via CSS if wanted.
local function j_pewist(text)
if not text then
return nil
end
return '<span class="j-pewist">' .. text .. "</span>"
end
local function err(text)
return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>"
end
-- Join a list with commas and a final "û" ("and"), e.g. {"Ehmed", "Berivan", "Rojin"} becomes
-- "Ehmed, Berivan û Rojin".
local function join_and(list)
if #list == 0 then
return nil
elseif #list == 1 then
return list[1]
elseif #list == 2 then
return list[1] .. " û " .. list[2]
else
return concat(list, ", ", 1, #list - 1) .. " û " .. list[#list]
end
end
-- Hidden marker expected by [[MediaWiki:Gadget-wikt.add-examples]] to locate this citation in the
-- rendered page; do NOT remove without updating that gadget too.
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
-- Resolve a language code to a language object, falling back to a tracking category (reusing the same
-- category the old {{jêder}} template used for a missing/bad language or missing second parameter) if it
-- can't be resolved.
local function get_langobj(langcode, categories)
if not langcode then
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then
return lang
end
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
------------------------------------------------------------------
-- Quoted-text portion (replaces {{jêder}}'s own {{zim}}/{{werger?}} logic)
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local jegirtin = args["jêgirtin"]
if not jegirtin then
return nil
end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = lang,
usex = jegirtin,
sc = sc,
transliteration = args.tr,
translation = args.werger,
quote = "quote-meta",
inline = true,
nocat = args.nekat,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Type-specific source-line formatters (replace {{pirtûk}}, {{kovar}}, {{malper}})
------------------------------------------------------------------
local function format_year(args)
local sal = args.sal
if not sal then
return nil
end
return j_pewist("'''" .. sal .. "'''")
end
local function format_kiteb(args, categories)
local parts = {}
local year = format_year(args)
if year then
insert(parts, year)
end
local authors = args["nivîskar"]
local amadekar = args.amadekar
local werger_val = args["wergêr"]
if #authors > 0 then
insert(parts, j_pewist(join_and(authors)))
end
if amadekar then
insert(parts, j_pewist("Amadekar: " .. amadekar))
end
if werger_val then
insert(parts, "(" .. j_pewist("terciman: " .. werger_val) .. ")")
end
local sernav = args.sernav
local url = args.url
if sernav then
local title = "''" .. sernav .. "''"
if url then
title = title .. "<sup>[" .. url .. "]</sup>"
end
insert(parts, title)
else
insert(parts, err("sernavê pirtûkê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
local sernav_orjinal = args["sernavê orjînal"]
if sernav_orjinal then
insert(parts, "[\"" .. sernav_orjinal .. "\"]")
end
local wergera_sernav = args["tercimeya sernav"]
if wergera_sernav then
insert(parts, "[\"" .. wergera_sernav .. "\"]")
end
local wesanxane = args["weşanxane"]
if wesanxane then
insert(parts, wesanxane)
end
local cih = args.cih
if cih then
insert(parts, cih)
end
local sala_wesandine = args["sala weşandinê"]
if sala_wesandine then
insert(parts, "sala weşandinê: " .. sala_wesandine)
end
local cap_val = args["çap"]
if cap_val then
insert(parts, "çapa " .. cap_val .. (cap_val == "1" and "ê" or "an"))
end
local cild = args.cild
if cild then
insert(parts, "cild " .. cild)
end
local rupel_val = args["rûpel"]
if rupel_val then
insert(parts, "r. " .. rupel_val)
end
local isbn = args.isbn
if isbn then
insert(parts, "ISBN " .. isbn)
end
return concat(parts, ", ")
end
local function format_kovar(args, categories)
local parts = {}
local year = format_year(args)
if year then
insert(parts, year)
end
local authors = args["nivîskar"]
local amadekar = args.amadekar
local werger_val = args["wergêr"]
if #authors > 0 then
insert(parts, j_pewist(join_and(authors)))
end
if amadekar then
insert(parts, j_pewist("Amadekar: " .. amadekar))
end
if werger_val then
insert(parts, "(" .. j_pewist("terciman: " .. werger_val) .. ")")
end
local sernav = args.sernav
local url = args.url
if sernav then
local title = "“" .. sernav .. "”"
if url then
title = title .. "<sup>[" .. url .. "]</sup>"
end
insert(parts, title)
end
local sernav_orjinal = args["sernavê orjînal"]
if sernav_orjinal then
insert(parts, "[\"" .. sernav_orjinal .. "\"]")
end
local kovar = args.kovar
if kovar then
insert(parts, "''" .. kovar .. "''")
else
insert(parts, err("navê kovarê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
local wesanxane = args["weşanxane"]
if wesanxane then
insert(parts, wesanxane)
end
local cild_val = args["cîld"]
if cild_val then
insert(parts, "cîld " .. cild_val)
end
local hejmar = args.hejmar
if hejmar then
insert(parts, "hejmar " .. hejmar)
end
local sala_wesandine = args["sala weşandinê"]
if sala_wesandine then
insert(parts, "sala weşandinê: " .. sala_wesandine)
end
local rupel_val = args["rûpel"]
if rupel_val then
insert(parts, "r. " .. rupel_val)
end
return concat(parts, ", ")
end
local function format_malper(args, categories)
local parts = {}
local nivIskar_val = join_and(args["nivîskar"])
local werger_val = args["wergêr"]
if nivIskar_val then
insert(parts, j_pewist(nivIskar_val))
end
if werger_val then
insert(parts, "(" .. j_pewist("terciman: " .. werger_val) .. ")")
end
local sernav = args.sernav
local url = args.url
if sernav then
local title = "“" .. sernav .. "”"
if url then
title = title .. "<sup>[" .. url .. "]</sup>"
end
insert(parts, title)
end
local malper = args.malper
if malper then
insert(parts, malper)
else
insert(parts, err("navê malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
if url then
local arsiv_val = args["arşîv"]
local link_text = " →<span class=\"plainlinks\"> [" .. url .. " lînk]</span>"
if arsiv_val then
link_text = link_text .. " [<span class=\"plainlinks\">[" .. arsiv_val .. " arşîv]</span>]"
else
link_text = link_text .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
insert(parts, link_text)
else
insert(parts, err("lînka malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
local sal = format_year(args)
if sal then
insert(parts, sal)
end
return concat(parts, ", ")
end
local format_by_kind = {
["kitêb"] = format_kiteb,
["kovar"] = format_kovar,
["malper"] = format_malper,
}
local kind_category_word = {
["kitêb"] = "kitêban",
["kovar"] = "kovaran",
["malper"] = "malperan",
}
------------------------------------------------------------------
-- Shared rendering logic
------------------------------------------------------------------
--[=[
Render a full citation.
* `raw_args` is the raw table of template arguments (frame:getParent().args is fine as-is; it is run
through [[Module:parameters]] here, which trims values, drops empties and resolves aliases).
* `kind` is one of "kitêb", "kovar", "malper", or nil for a bare {{jêder}} call, in which case |2= is
expected to already be fully-formatted wikitext (kept only for backward compatibility with any page
that still calls {{jêder}} directly with a hand-built |2=; new citations should use |kind=).
]=]
function export.render(raw_args, kind)
-- return_unknown = true -> unrecognised/legacy params are ignored, not turned into red errors.
local args = process_params(raw_args, params, true)
local categories = {}
local langcode = args[1]
local lang = get_langobj(langcode, categories)
local source_text
if kind then
source_text = format_by_kind[kind](args, categories)
else
source_text = args[2]
if not source_text then
source_text = err("divê parametreya duyemîn (çavkanî) were dayîn")
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
end
local quote_text = build_quote(args, lang, langcode) or ""
-- {{jêder-malper}}/format_malper already builds its own URL/archive link inline, since a website
-- citation's whole point is the link; for the other two kinds we add it here, exactly like the old
-- {{jêder}} template did.
local link_text = ""
if kind ~= "malper" then
local url = args.url
if url then
local arsiv_val = args["arşîv"]
link_text = " →<span class=\"plainlinks\"> [" .. url .. " here lînkê]</span>"
if arsiv_val then
link_text = link_text .. " [<span class=\"plainlinks\">[" .. arsiv_val .. " arşîv]</span>]"
else
link_text = link_text .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
end
end
local nekat = args.nekat
local cat_text = ""
if not nekat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
if kind then
insert(cats, "Jêgirtinên " .. kind_category_word[kind] .. " bi " .. langname)
end
end
for _, c in ipairs(categories) do
insert(cats, c)
end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. link_text .. ")</span>"
.. cat_text
end
------------------------------------------------------------------
-- Template entry points
------------------------------------------------------------------
-- {{jêder}}: kept for backward compatibility (expects a hand-built |2=).
function export.jeder_t(frame)
return export.render(frame:getParent().args, nil)
end
-- {{jêder-kitêb}}
function export.kiteb_t(frame)
return export.render(frame:getParent().args, "kitêb")
end
-- {{jêder-kovar}}
function export.kovar_t(frame)
return export.render(frame:getParent().args, "kovar")
end
-- {{jêder-malper}}
function export.malper_t(frame)
return export.render(frame:getParent().args, "malper")
end
return export
o7t416jjk803tki0n5vfx5bf25x0sv2
6276973
6276971
2026-07-23T21:24:33Z
Wikihez
7344
6276973
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — unifies {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}},
{{jêder-malper}} into one #invoke-based module.
WHAT CHANGED IN THIS VERSION
----------------------------
1. English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are now the CANONICAL
names, and the old Kurdish names are kept as ALIASES. This means an English quotation template can
be pasted in almost verbatim — |author=, |title=, |publisher=, |page=, |year=, |isbn= ... all work —
while every existing Kurdish entry (|nivîskar=, |sernav=, |weşanxane=, |rûpel=, |sal= ...) keeps
working unchanged. See the `params` table: adding another parameter is still a one-line change.
2. The source line is no longer a naive comma-join. It is now built in en.wiktionary bibliographic
ORDER and STYLE — bold year, author list, "quoted" chapter/article, ''italic'' book/journal title,
edition/volume/issue, "cih: weşanxane" (location: publisher), page(s), and arrow-linked identifiers
(→ISBN, →ISSN, →OCLC, →DOI, →JSTOR, →LCCN, →PMID, →PMC ...). The connector WORDS stay Kurdish
(r., cild, hejmar, çap, di ... de) because this is the Kurdish Wiktionary; only the layout follows
en.wikt.
Argument handling goes through [[Module:parameters]] (same as [[Module:quote]]); it trims values, drops
empties, resolves aliases and, with return_unknown = true, lets us flag any undeclared parameter as a
red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of silently ignoring a typo.
The quoted-text portion (|text=/|passage=/|jêgirtin=, |tr=, |t=/|werger=, ...) is delegated to
[[Module:usex/ceribandin]] — the same module [[Module:quote]] uses — for script tagging, translit
fallback and translation-request categorization.
The hidden <bdi class="example">…</bdi> marker used by [[MediaWiki:Gadget-wikt.add-examples]] is
preserved; do NOT remove it without updating that gadget.
NOTE / SCOPE: this is a self-contained re-implementation of en.wikt's LAYOUT, not a byte-for-byte port
of Module:quote's 3,500-line engine. The full practical parameter set is accepted and the common
bibliographic fields + identifiers are rendered in en.wikt style. A few exotic Module:quote params
(the newversion "…2" republication set, video-game scene/level, section sub-series, etc.) are accepted
so pasted templates don't error, but are not yet individually rendered — add them to format_source()
as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
-- Lazy loaders (same pattern as [[Module:quote]]).
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function track(page)
debug_track("jêder/" .. page)
end
------------------------------------------------------------------
-- Parameter specification (single source of truth)
------------------------------------------------------------------
-- English names are canonical; Kurdish names (and en.wikt's own English aliases) are alias_of them.
-- To add a parameter: a plain field = true; an alias = {alias_of="canon"}; a numbered run = {list=true}.
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true, -- language code (resolved manually)
["lang"] = true, -- |lang= accepted as an alternative to |1=
-- quoted text (delegated to Module:usex/ceribandin)
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true,
["transliteration"] = {alias_of = "tr"},
["ts"] = true,
["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"},
["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"},
["w"] = {alias_of = "t"},
["tercime"] = {alias_of = "t"},
["norm"] = true,
["subst"] = true,
["lit"] = true,
["lb"] = true,
["footer"] = true,
["brackets"] = boolean,
["termlang"] = true,
["nocat"] = boolean,
["nekat"] = {alias_of = "nocat"},
["noreq"] = boolean,
-- original-text quote params (passed through to usex)
["origtext"] = true,
["origtr"] = true,
["origts"] = true,
["orignorm"] = true,
["origsc"] = true,
["origlb"] = true,
["origlang"] = true,
["sc"] = true,
-- people
["author"] = list,
-- an alias of a list parameter must itself be declared list (Module:parameters rule), so that
-- |nivîskar=, |nivîskar2=, ... feed into the same list as |author=, |author2=, ...
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list,
["last"] = list,
["first"] = list,
["2ndauthor"] = true,
["coauthors"] = true,
["mainauthor"] = true,
["editor"] = true,
["editors"] = true,
["amadekar"] = {alias_of = "editor"},
["tlr"] = true, -- translator
["translator"] = {alias_of = "tlr"},
["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"},
["terciman"] = {alias_of = "tlr"},
["compiler"] = true,
["compilers"] = true,
["director"] = true,
["directors"] = true,
["quotee"] = true,
["others"] = true,
["by"] = true,
["role"] = true,
["speaker"] = {alias_of = "role"},
["actor"] = true,
["artist"] = true,
["composer"] = true,
["lyricist"] = true,
-- title / container
["title"] = true,
["sernav"] = {alias_of = "title"},
["trans-title"] = true,
["tercimeya sernav"] = {alias_of = "trans-title"},
["wergera sernav"] = {alias_of = "trans-title"},
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true,
["trans-chapter"] = true,
["chapterurl"] = true,
["chapter_plain"] = true,
["entry"] = true,
["entryurl"] = true,
["trans-entry"] = true,
["section"] = true,
["sectionurl"] = true,
["series"] = true,
["seriesvolume"] = true,
["journal"] = true,
["kovar"] = {alias_of = "journal"},
["work"] = true,
["website"] = true,
["site"] = {alias_of = "website"},
["malper"] = {alias_of = "website"},
["websîte"] = {alias_of = "website"},
["notitle"] = boolean,
-- edition / numbering
["edition"] = true,
["çap"] = {alias_of = "edition"},
["edition_plain"] = true,
["volume"] = true,
["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"},
["cîld"] = {alias_of = "volume"},
["volume_plain"] = true,
["issue"] = true,
["hejmar"] = {alias_of = "issue"},
["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true,
["rûpel"] = {alias_of = "page"},
["r"] = {alias_of = "page"},
["pages"] = true,
["pageurl"] = true,
["column"] = true,
["line"] = true,
["lines"] = true,
-- publication facts
["location"] = true,
["cih"] = {alias_of = "location"},
["publisher"] = true,
["weşanxane"] = {alias_of = "publisher"},
["year"] = true,
["sal"] = {alias_of = "year"},
["date"] = true,
["dîrok"] = {alias_of = "date"},
["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true,
["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true,
["start_year"] = true,
["origdate"] = true,
["origyear"] = true,
["accessdate"] = true,
["archivedate"] = true,
["nodate"] = boolean,
["type"] = true,
["genre"] = true,
["format"] = true,
["medium"] = true,
-- URLs
["url"] = true,
["urls"] = true,
["archiveurl"] = true,
["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
-- backward-compat bare {{jêder}}: pre-built source text
[2] = true,
}
-- Maintenance category for citations given a parameter that isn't declared above.
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text)
if not text then return nil end
return '<span class="j-pewist">' .. text .. "</span>"
end
local function err(text)
return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>"
end
-- Kurdish "and"-join: {"a","b","c"} -> "a, b û c".
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
-- Bold the first 4-digit year inside a date string, e.g. "15 Gulan 2020" -> "15 Gulan '''2020'''".
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
-- A title with an optional superscript external-link marker (en.wikt puts the url on the title).
local function linked_title(title, url)
if url then
return title .. "<sup>[" .. url .. "]</sup>"
end
return title
end
------------------------------------------------------------------
-- Identifier links (en.wikt style: a short arrow label linking out)
------------------------------------------------------------------
local function id_isbn(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end
local function id_issn(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end
local function id_oclc(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end
local function id_doi(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end
local function id_jstor(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end
local function id_lccn(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end
local function id_pmid(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end
local function id_pmc(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end
local function id_bibcode(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end
local function id_ol(v) return "[https://openlibrary.org/" .. v .. " →OL]" end
local function id_ssrn(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end
-- ordered so identifiers always come out in the same sequence
local id_order = {
{"isbn", id_isbn}, {"issn", id_issn}, {"oclc", id_oclc}, {"doi", id_doi},
{"jstor", id_jstor}, {"lccn", id_lccn}, {"pmid", id_pmid}, {"pmcid", id_pmc},
{"bibcode", id_bibcode}, {"ol", id_ol}, {"ssrn", id_ssrn},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text,
sc = sc,
transliteration = args.tr,
transcription = args.ts,
translation = args.t,
normalization = args.norm,
subst = args.subst,
lit = args.lit,
brackets = args.brackets,
quote = "quote-meta",
inline = true,
nocat = args.nocat,
noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, built in en.wikt bibliographic order
------------------------------------------------------------------
-- `kind`: "kitêb" (book) -> title is the italic work
-- "kovar" (journal)-> title is the "quoted" article, journal is the italic work
-- "malper" (web) -> title is the "quoted" page, website/work is the container, url is prominent
-- nil (bare jêder) -> caller supplies pre-built |2=
local function format_source(args, kind, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year), in parentheses like en.wikt
local datestr = args.date and bold_year(args.date)
or (args.year and "'''" .. args.year .. "'''")
or nil
add(datestr)
-- 2. author(s), with role helpers
local authors = join_and(args.author)
if not authors and args.last and #args.last > 0 then
-- assemble "Last, First" pairs when split-name params were used
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
authors = join_and(names)
end
add(j_pewist(authors))
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
if args.editor or args.editors then
add(j_pewist("edîtor: " .. (args.editor or args.editors)))
end
if args.tlr then
add("(" .. j_pewist("terciman: " .. args.tlr) .. ")")
end
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter / entry / section ("quoted"), with optional url
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
if chapter then
add(linked_title("“" .. chapter .. "”", chapterurl))
if args["trans-chapter"] or args["trans-entry"] then
add("[“" .. (args["trans-chapter"] or args["trans-entry"]) .. "”]")
end
end
-- 4. title (italic for a book/standalone work; quoted for a journal article / web page)
local title = args.title
if title then
local styled
if kind == "kovar" or kind == "malper" then
styled = "“" .. title .. "”"
else
styled = "''" .. title .. "''"
end
add(linked_title(styled, args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
elseif kind == "kitêb" then
add(err("sernavê pirtûkê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
-- 5. the container work: journal (italic) / website, introduced with "di … de" (in …)
if kind == "kovar" then
local journal = args.journal
if journal then
add("di ''" .. journal .. "'' de")
else
add(err("navê kovarê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
elseif kind == "malper" then
local wsite = args.website or args.work
if wsite then
add("li ser " .. wsite)
elseif not title then
add(err("navê malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
else
-- book/bare: a journal/work container may still be given
add(args.journal and ("di ''" .. args.journal .. "'' de") or nil)
end
-- 6. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 7. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 8. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 9. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location)
add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 10. page(s) / column / line
if args.pages then
add("rr. " .. args.pages)
elseif args.page then
add("r. " .. args.page)
end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 11. type / genre / format / medium
add(args.type)
add(args.genre)
add(args.medium)
add(args.format)
-- 12. identifiers, always in a fixed order
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 13. url / archive (for web the url is on the title above; here we add the archive + bare url fallback)
local url = args.url
if url and kind == "malper" then
local link = " →<span class=\"plainlinks\"> [" .. url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
add(link)
elseif not title and url then
-- no title to hang the url on; add it plainly
add("<span class=\"plainlinks\">[" .. url .. " lînk]</span>")
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
["kitêb"] = "kitêban",
["kovar"] = "kovaran",
["malper"] = "malperan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, kind)
local args, unknown = process_params(raw_args, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text
if kind then
source_text = format_source(args, kind, categories)
else
source_text = args[2]
if not source_text then
source_text = err("divê parametreya duyemîn (çavkanî) were dayîn")
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
end
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
if kind then
insert(cats, "Jêgirtinên " .. kind_category_word[kind] .. " bi " .. langname)
end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points
------------------------------------------------------------------
function export.jeder_t(frame) return export.render(frame:getParent().args, nil) end
function export.kiteb_t(frame) return export.render(frame:getParent().args, "kitêb") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "kovar") end
function export.malper_t(frame) return export.render(frame:getParent().args, "malper") end
return export
ms0gex1a9fohnst8ypgt50q49e84z9h
6276975
6276973
2026-07-23T21:38:45Z
Wikihez
7344
6276975
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — unifies {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}},
{{jêder-malper}} into one #invoke-based module.
WHAT CHANGED IN THIS VERSION
----------------------------
1. English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are now the CANONICAL
names, and the old Kurdish names are kept as ALIASES. This means an English quotation template can
be pasted in almost verbatim — |author=, |title=, |publisher=, |page=, |year=, |isbn= ... all work —
while every existing Kurdish entry (|nivîskar=, |sernav=, |weşanxane=, |rûpel=, |sal= ...) keeps
working unchanged. See the `params` table: adding another parameter is still a one-line change.
2. The source line is no longer a naive comma-join. It is now built in en.wiktionary bibliographic
ORDER and STYLE — bold year, author list, "quoted" chapter/article, ''italic'' book/journal title,
edition/volume/issue, "cih: weşanxane" (location: publisher), page(s), and arrow-linked identifiers
(→ISBN, →ISSN, →OCLC, →DOI, →JSTOR, →LCCN, →PMID, →PMC ...). The connector WORDS stay Kurdish
(r., cild, hejmar, çap, di ... de) because this is the Kurdish Wiktionary; only the layout follows
en.wikt.
Argument handling goes through [[Module:parameters]] (same as [[Module:quote]]); it trims values, drops
empties, resolves aliases and, with return_unknown = true, lets us flag any undeclared parameter as a
red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of silently ignoring a typo.
The quoted-text portion (|text=/|passage=/|jêgirtin=, |tr=, |t=/|werger=, ...) is delegated to
[[Module:usex/ceribandin]] — the same module [[Module:quote]] uses — for script tagging, translit
fallback and translation-request categorization.
The hidden <bdi class="example">…</bdi> marker used by [[MediaWiki:Gadget-wikt.add-examples]] is
preserved; do NOT remove it without updating that gadget.
NOTE / SCOPE: this is a self-contained re-implementation of en.wikt's LAYOUT, not a byte-for-byte port
of Module:quote's 3,500-line engine. The full practical parameter set is accepted and the common
bibliographic fields + identifiers are rendered in en.wikt style. A few exotic Module:quote params
(the newversion "…2" republication set, video-game scene/level, section sub-series, etc.) are accepted
so pasted templates don't error, but are not yet individually rendered — add them to format_source()
as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
-- Lazy loaders (same pattern as [[Module:quote]]).
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function track(page)
debug_track("jêder/" .. page)
end
------------------------------------------------------------------
-- Parameter specification (single source of truth)
------------------------------------------------------------------
-- English names are canonical; Kurdish names (and en.wikt's own English aliases) are alias_of them.
-- To add a parameter: a plain field = true; an alias = {alias_of="canon"}; a numbered run = {list=true}.
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true, -- language code (resolved manually)
["lang"] = true, -- |lang= accepted as an alternative to |1=
-- quoted text (delegated to Module:usex/ceribandin)
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true,
["transliteration"] = {alias_of = "tr"},
["ts"] = true,
["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"},
["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"},
["w"] = {alias_of = "t"},
["tercime"] = {alias_of = "t"},
["norm"] = true,
["subst"] = true,
["lit"] = true,
["lb"] = true,
["footer"] = true,
["brackets"] = boolean,
["termlang"] = true,
["nocat"] = boolean,
["nekat"] = {alias_of = "nocat"},
["noreq"] = boolean,
-- original-text quote params (passed through to usex)
["origtext"] = true,
["origtr"] = true,
["origts"] = true,
["orignorm"] = true,
["origsc"] = true,
["origlb"] = true,
["origlang"] = true,
["sc"] = true,
-- people
["author"] = list,
-- an alias of a list parameter must itself be declared list (Module:parameters rule), so that
-- |nivîskar=, |nivîskar2=, ... feed into the same list as |author=, |author2=, ...
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list,
["last"] = list,
["first"] = list,
["2ndauthor"] = true,
["coauthors"] = true,
["mainauthor"] = true,
["editor"] = true,
["editors"] = true,
["amadekar"] = {alias_of = "editor"},
["tlr"] = true, -- translator
["translator"] = {alias_of = "tlr"},
["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"},
["terciman"] = {alias_of = "tlr"},
["compiler"] = true,
["compilers"] = true,
["director"] = true,
["directors"] = true,
["quotee"] = true,
["others"] = true,
["by"] = true,
["role"] = true,
["speaker"] = {alias_of = "role"},
["actor"] = true,
["artist"] = true,
["composer"] = true,
["lyricist"] = true,
-- title / container
["title"] = true,
["sernav"] = {alias_of = "title"},
["trans-title"] = true,
["tercimeya sernav"] = {alias_of = "trans-title"},
["wergera sernav"] = {alias_of = "trans-title"},
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true,
["trans-chapter"] = true,
["chapterurl"] = true,
["chapter_plain"] = true,
["entry"] = true,
["entryurl"] = true,
["trans-entry"] = true,
["section"] = true,
["sectionurl"] = true,
["series"] = true,
["seriesvolume"] = true,
["journal"] = true,
["kovar"] = {alias_of = "journal"},
["work"] = true,
["website"] = true,
["site"] = {alias_of = "website"},
["malper"] = {alias_of = "website"},
["websîte"] = {alias_of = "website"},
["notitle"] = boolean,
-- edition / numbering
["edition"] = true,
["çap"] = {alias_of = "edition"},
["edition_plain"] = true,
["volume"] = true,
["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"},
["cîld"] = {alias_of = "volume"},
["volume_plain"] = true,
["issue"] = true,
["hejmar"] = {alias_of = "issue"},
["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true,
["rûpel"] = {alias_of = "page"},
["r"] = {alias_of = "page"},
["pages"] = true,
["pageurl"] = true,
["column"] = true,
["line"] = true,
["lines"] = true,
-- publication facts
["location"] = true,
["cih"] = {alias_of = "location"},
["publisher"] = true,
["weşanxane"] = {alias_of = "publisher"},
["year"] = true,
["sal"] = {alias_of = "year"},
["date"] = true,
["dîrok"] = {alias_of = "date"},
["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true,
["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true,
["start_year"] = true,
["origdate"] = true,
["origyear"] = true,
["accessdate"] = true,
["archivedate"] = true,
["nodate"] = boolean,
["type"] = true,
["genre"] = true,
["format"] = true,
["medium"] = true,
-- URLs
["url"] = true,
["urls"] = true,
["archiveurl"] = true,
["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
-- backward-compat bare {{jêder}}: pre-built source text
[2] = true,
}
-- Maintenance category for citations given a parameter that isn't declared above.
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text)
if not text then return nil end
return '<span class="j-pewist">' .. text .. "</span>"
end
local function err(text)
return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>"
end
-- Kurdish "and"-join: {"a","b","c"} -> "a, b û c".
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
-- Bold the first 4-digit year inside a date string, e.g. "15 Gulan 2020" -> "15 Gulan '''2020'''".
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
-- A title with an optional superscript external-link marker (en.wikt puts the url on the title).
local function linked_title(title, url)
if url then
return title .. "<sup>[" .. url .. "]</sup>"
end
return title
end
------------------------------------------------------------------
-- Identifier links (en.wikt style: a short arrow label linking out)
------------------------------------------------------------------
local function id_isbn(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end
local function id_issn(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end
local function id_oclc(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end
local function id_doi(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end
local function id_jstor(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end
local function id_lccn(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end
local function id_pmid(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end
local function id_pmc(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end
local function id_bibcode(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end
local function id_ol(v) return "[https://openlibrary.org/" .. v .. " →OL]" end
local function id_ssrn(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end
-- ordered so identifiers always come out in the same sequence
local id_order = {
{"isbn", id_isbn}, {"issn", id_issn}, {"oclc", id_oclc}, {"doi", id_doi},
{"jstor", id_jstor}, {"lccn", id_lccn}, {"pmid", id_pmid}, {"pmcid", id_pmc},
{"bibcode", id_bibcode}, {"ol", id_ol}, {"ssrn", id_ssrn},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text,
sc = sc,
transliteration = args.tr,
transcription = args.ts,
translation = args.t,
normalization = args.norm,
subst = args.subst,
lit = args.lit,
brackets = args.brackets,
quote = "quote-meta",
inline = true,
nocat = args.nocat,
noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, built in en.wikt bibliographic order
------------------------------------------------------------------
-- `kind`: "kitêb" (book) -> title is the italic work
-- "kovar" (journal)-> title is the "quoted" article, journal is the italic work
-- "malper" (web) -> title is the "quoted" page, website/work is the container, url is prominent
-- nil (bare jêder) -> caller supplies pre-built |2=
local function format_source(args, kind, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year), in parentheses like en.wikt
local datestr = args.date and bold_year(args.date)
or (args.year and "'''" .. args.year .. "'''")
or nil
add(datestr)
-- 2. author(s), with role helpers
local authors = join_and(args.author)
if not authors and args.last and #args.last > 0 then
-- assemble "Last, First" pairs when split-name params were used
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
authors = join_and(names)
end
add(j_pewist(authors))
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
if args.editor or args.editors then
add(j_pewist("edîtor: " .. (args.editor or args.editors)))
end
if args.tlr then
add("(" .. j_pewist("terciman: " .. args.tlr) .. ")")
end
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter / entry / section ("quoted"), with optional url
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
if chapter then
add(linked_title("“" .. chapter .. "”", chapterurl))
if args["trans-chapter"] or args["trans-entry"] then
add("[“" .. (args["trans-chapter"] or args["trans-entry"]) .. "”]")
end
end
-- 4. title (italic for a book/standalone work; quoted for a journal article / web page)
local title = args.title
if title then
local styled
if kind == "kovar" or kind == "malper" then
styled = "“" .. title .. "”"
else
styled = "''" .. title .. "''"
end
add(linked_title(styled, args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
elseif kind == "kitêb" then
add(err("sernavê pirtûkê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
-- 5. the container work: journal (italic) / website, introduced with "di … de" (in …)
if kind == "kovar" then
local journal = args.journal
if journal then
add("di ''" .. journal .. "'' de")
else
add(err("navê kovarê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
elseif kind == "malper" then
local wsite = args.website or args.work
if wsite then
add("li ser " .. wsite)
elseif not title then
add(err("navê malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
else
-- book/bare: a journal/work container may still be given
add(args.journal and ("di ''" .. args.journal .. "'' de") or nil)
end
-- 6. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 7. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 8. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 9. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location)
add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 10. page(s) / column / line
if args.pages then
add("rr. " .. args.pages)
elseif args.page then
add("r. " .. args.page)
end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 11. type / genre / format / medium
add(args.type)
add(args.genre)
add(args.medium)
add(args.format)
-- 12. identifiers, always in a fixed order
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 13. url / archive (for web the url is on the title above; here we add the archive + bare url fallback)
local url = args.url
if url and kind == "malper" then
local link = " →<span class=\"plainlinks\"> [" .. url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
add(link)
elseif not title and url then
-- no title to hang the url on; add it plainly
add("<span class=\"plainlinks\">[" .. url .. " lînk]</span>")
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
["kitêb"] = "kitêban",
["kovar"] = "kovaran",
["malper"] = "malperan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, kind)
local args, unknown = process_params(raw_args, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text
if kind then
source_text = format_source(args, kind, categories)
else
source_text = args[2]
if not source_text then
source_text = err("divê parametreya duyemîn (çavkanî) were dayîn")
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
end
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
if kind then
insert(cats, "Jêgirtinên " .. kind_category_word[kind] .. " bi " .. langname)
end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points
------------------------------------------------------------------
function export.jeder_t(frame) return export.render(frame:getParent().args, nil) end
function export.kiteb_t(frame) return export.render(frame:getParent().args, "kitêb") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "kovar") end
function export.malper_t(frame) return export.render(frame:getParent().args, "malper") end
return export
q6gjbzt37xwwsh4zfs3zkdd4syo0ttu
6276979
6276975
2026-07-23T21:58:30Z
Wikihez
7344
6276979
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — unifies {{jêder}}, {{jêder-kitêb}}, {{jêder-kovar}},
{{jêder-malper}} into one #invoke-based module.
WHAT CHANGED IN THIS VERSION
----------------------------
1. English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are now the CANONICAL
names, and the old Kurdish names are kept as ALIASES. This means an English quotation template can
be pasted in almost verbatim — |author=, |title=, |publisher=, |page=, |year=, |isbn= ... all work —
while every existing Kurdish entry (|nivîskar=, |sernav=, |weşanxane=, |rûpel=, |sal= ...) keeps
working unchanged. See the `params` table: adding another parameter is still a one-line change.
2. The source line is no longer a naive comma-join. It is now built in en.wiktionary bibliographic
ORDER and STYLE — bold year, author list, "quoted" chapter/article, ''italic'' book/journal title,
edition/volume/issue, "cih: weşanxane" (location: publisher), page(s), and arrow-linked identifiers
(→ISBN, →ISSN, →OCLC, →DOI, →JSTOR, →LCCN, →PMID, →PMC ...). The connector WORDS stay Kurdish
(r., cild, hejmar, çap, di ... de) because this is the Kurdish Wiktionary; only the layout follows
en.wikt.
Argument handling goes through [[Module:parameters]] (same as [[Module:quote]]); it trims values, drops
empties, resolves aliases and, with return_unknown = true, lets us flag any undeclared parameter as a
red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of silently ignoring a typo.
The quoted-text portion (|text=/|passage=/|jêgirtin=, |tr=, |t=/|werger=, ...) is delegated to
[[Module:usex/ceribandin]] — the same module [[Module:quote]] uses — for script tagging, translit
fallback and translation-request categorization.
The hidden <bdi class="example">…</bdi> marker used by [[MediaWiki:Gadget-wikt.add-examples]] is
preserved; do NOT remove it without updating that gadget.
NOTE / SCOPE: this is a self-contained re-implementation of en.wikt's LAYOUT, not a byte-for-byte port
of Module:quote's 3,500-line engine. The full practical parameter set is accepted and the common
bibliographic fields + identifiers are rendered in en.wikt style. A few exotic Module:quote params
(the newversion "…2" republication set, video-game scene/level, section sub-series, etc.) are accepted
so pasted templates don't error, but are not yet individually rendered — add them to format_source()
as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
-- Lazy loaders (same pattern as [[Module:quote]]).
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function track(page)
debug_track("jêder/" .. page)
end
------------------------------------------------------------------
-- Parameter specification (single source of truth)
------------------------------------------------------------------
-- English names are canonical; Kurdish names (and en.wikt's own English aliases) are alias_of them.
-- To add a parameter: a plain field = true; an alias = {alias_of="canon"}; a numbered run = {list=true}.
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true, -- language code (resolved manually)
["lang"] = true, -- |lang= accepted as an alternative to |1=
-- quoted text (delegated to Module:usex/ceribandin)
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true,
["transliteration"] = {alias_of = "tr"},
["ts"] = true,
["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"},
["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"},
["w"] = {alias_of = "t"},
["tercime"] = {alias_of = "t"},
["norm"] = true,
["subst"] = true,
["lit"] = true,
["lb"] = true,
["footer"] = true,
["brackets"] = boolean,
["termlang"] = true,
["nocat"] = boolean,
["nekat"] = {alias_of = "nocat"},
["noreq"] = boolean,
-- original-text quote params (passed through to usex)
["origtext"] = true,
["origtr"] = true,
["origts"] = true,
["orignorm"] = true,
["origsc"] = true,
["origlb"] = true,
["origlang"] = true,
["sc"] = true,
-- people
["author"] = list,
-- an alias of a list parameter must itself be declared list (Module:parameters rule), so that
-- |nivîskar=, |nivîskar2=, ... feed into the same list as |author=, |author2=, ...
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list,
["last"] = list,
["first"] = list,
["2ndauthor"] = true,
["coauthors"] = true,
["mainauthor"] = true,
["editor"] = true,
["editors"] = true,
["amadekar"] = {alias_of = "editor"},
["tlr"] = true, -- translator
["translator"] = {alias_of = "tlr"},
["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"},
["terciman"] = {alias_of = "tlr"},
["compiler"] = true,
["compilers"] = true,
["director"] = true,
["directors"] = true,
["quotee"] = true,
["others"] = true,
["by"] = true,
["role"] = true,
["speaker"] = {alias_of = "role"},
["actor"] = true,
["artist"] = true,
["composer"] = true,
["lyricist"] = true,
-- title / container
["title"] = true,
["sernav"] = {alias_of = "title"},
["trans-title"] = true,
["tercimeya sernav"] = {alias_of = "trans-title"},
["wergera sernav"] = {alias_of = "trans-title"},
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true,
["trans-chapter"] = true,
["chapterurl"] = true,
["chapter_plain"] = true,
["entry"] = true,
["entryurl"] = true,
["trans-entry"] = true,
["section"] = true,
["sectionurl"] = true,
["series"] = true,
["seriesvolume"] = true,
["journal"] = true,
["kovar"] = {alias_of = "journal"},
["work"] = true,
["website"] = true,
["site"] = {alias_of = "website"},
["malper"] = {alias_of = "website"},
["websîte"] = {alias_of = "website"},
["notitle"] = boolean,
-- edition / numbering
["edition"] = true,
["çap"] = {alias_of = "edition"},
["edition_plain"] = true,
["volume"] = true,
["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"},
["cîld"] = {alias_of = "volume"},
["volume_plain"] = true,
["issue"] = true,
["hejmar"] = {alias_of = "issue"},
["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true,
["rûpel"] = {alias_of = "page"},
["r"] = {alias_of = "page"},
["pages"] = true,
["pageurl"] = true,
["column"] = true,
["line"] = true,
["lines"] = true,
-- publication facts
["location"] = true,
["cih"] = {alias_of = "location"},
["publisher"] = true,
["weşanxane"] = {alias_of = "publisher"},
["year"] = true,
["sal"] = {alias_of = "year"},
["date"] = true,
["dîrok"] = {alias_of = "date"},
["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true,
["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true,
["start_year"] = true,
["origdate"] = true,
["origyear"] = true,
["accessdate"] = true,
["archivedate"] = true,
["nodate"] = boolean,
["type"] = true,
["genre"] = true,
["format"] = true,
["medium"] = true,
-- URLs
["url"] = true,
["urls"] = true,
["archiveurl"] = true,
["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
-- backward-compat bare {{jêder}}: pre-built source text
[2] = true,
}
-- Maintenance category for citations given a parameter that isn't declared above.
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text)
if not text then return nil end
return '<span class="j-pewist">' .. text .. "</span>"
end
local function err(text)
return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>"
end
-- Kurdish "and"-join: {"a","b","c"} -> "a, b û c".
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
-- Bold the first 4-digit year inside a date string, e.g. "15 Gulan 2020" -> "15 Gulan '''2020'''".
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, "Şablon:jêder parametre duyem kêm e")
return nil
end
-- A title with an optional superscript external-link marker (en.wikt puts the url on the title).
local function linked_title(title, url)
if url then
return title .. "<sup>[" .. url .. "]</sup>"
end
return title
end
------------------------------------------------------------------
-- Identifier links (en.wikt style: a short arrow label linking out)
------------------------------------------------------------------
local function id_isbn(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end
local function id_issn(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end
local function id_oclc(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end
local function id_doi(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end
local function id_jstor(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end
local function id_lccn(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end
local function id_pmid(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end
local function id_pmc(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end
local function id_bibcode(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end
local function id_ol(v) return "[https://openlibrary.org/" .. v .. " →OL]" end
local function id_ssrn(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end
-- ordered so identifiers always come out in the same sequence
local id_order = {
{"isbn", id_isbn}, {"issn", id_issn}, {"oclc", id_oclc}, {"doi", id_doi},
{"jstor", id_jstor}, {"lccn", id_lccn}, {"pmid", id_pmid}, {"pmcid", id_pmc},
{"bibcode", id_bibcode}, {"ol", id_ol}, {"ssrn", id_ssrn},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text,
sc = sc,
transliteration = args.tr,
transcription = args.ts,
translation = args.t,
normalization = args.norm,
subst = args.subst,
lit = args.lit,
brackets = args.brackets,
quote = "quote-meta",
inline = false,
nocat = args.nocat,
noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, built in en.wikt bibliographic order
------------------------------------------------------------------
-- `kind`: "kitêb" (book) -> title is the italic work
-- "kovar" (journal)-> title is the "quoted" article, journal is the italic work
-- "malper" (web) -> title is the "quoted" page, website/work is the container, url is prominent
-- nil (bare jêder) -> caller supplies pre-built |2=
local function format_source(args, kind, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year), in parentheses like en.wikt
local datestr = args.date and bold_year(args.date)
or (args.year and "'''" .. args.year .. "'''")
or nil
add(datestr)
-- 2. author(s), with role helpers
local authors = join_and(args.author)
if not authors and args.last and #args.last > 0 then
-- assemble "Last, First" pairs when split-name params were used
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
authors = join_and(names)
end
add(j_pewist(authors))
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
if args.editor or args.editors then
add(j_pewist("edîtor: " .. (args.editor or args.editors)))
end
if args.tlr then
add("(" .. j_pewist("terciman: " .. args.tlr) .. ")")
end
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter / entry / section ("quoted"), with optional url
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
if chapter then
add(linked_title("“" .. chapter .. "”", chapterurl))
if args["trans-chapter"] or args["trans-entry"] then
add("[“" .. (args["trans-chapter"] or args["trans-entry"]) .. "”]")
end
end
-- 4. title (italic for a book/standalone work; quoted for a journal article / web page)
local title = args.title
if title then
local styled
if kind == "kovar" or kind == "malper" then
styled = "“" .. title .. "”"
else
styled = "''" .. title .. "''"
end
add(linked_title(styled, args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
elseif kind == "kitêb" then
add(err("sernavê pirtûkê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
-- 5. the container work: journal (italic) / website, introduced with "di … de" (in …)
if kind == "kovar" then
local journal = args.journal
if journal then
add("di ''" .. journal .. "'' de")
else
add(err("navê kovarê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
elseif kind == "malper" then
local wsite = args.website or args.work
if wsite then
add("li ser " .. wsite)
elseif not title then
add(err("navê malperê kêm e"))
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
else
-- book/bare: a journal/work container may still be given
add(args.journal and ("di ''" .. args.journal .. "'' de") or nil)
end
-- 6. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 7. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 8. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 9. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location)
add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 10. page(s) / column / line
if args.pages then
add("rr. " .. args.pages)
elseif args.page then
add("r. " .. args.page)
end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 11. type / genre / format / medium
add(args.type)
add(args.genre)
add(args.medium)
add(args.format)
-- 12. identifiers, always in a fixed order
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 13. url / archive (for web the url is on the title above; here we add the archive + bare url fallback)
local url = args.url
if url and kind == "malper" then
local link = " →<span class=\"plainlinks\"> [" .. url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. url .. " arşîv]</span>]"
end
add(link)
elseif not title and url then
-- no title to hang the url on; add it plainly
add("<span class=\"plainlinks\">[" .. url .. " lînk]</span>")
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
["kitêb"] = "kitêban",
["kovar"] = "kovaran",
["malper"] = "malperan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, kind)
local args, unknown = process_params(raw_args, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text
if kind then
source_text = format_source(args, kind, categories)
else
source_text = args[2]
if not source_text then
source_text = err("divê parametreya duyemîn (çavkanî) were dayîn")
insert(categories, "Şablon:jêder parametre duyem kêm e")
end
end
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
if kind then
insert(cats, "Jêgirtinên " .. kind_category_word[kind] .. " bi " .. langname)
end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points
------------------------------------------------------------------
function export.jeder_t(frame) return export.render(frame:getParent().args, nil) end
function export.kiteb_t(frame) return export.render(frame:getParent().args, "kitêb") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "kovar") end
function export.malper_t(frame) return export.render(frame:getParent().args, "malper") end
return export
amdd93s8hlx2uei4dz5j0yabvlxph72
6276981
6276979
2026-07-23T22:15:06Z
Wikihez
7344
6276981
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = true, nocat = args.nocat, noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''") or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
rd7zog2t9jfsi01qhy45057ycc24ffj
6276982
6276981
2026-07-23T22:17:37Z
Wikihez
7344
6276982
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = false, nocat = args.nocat, noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''") or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
7if1xaxf4z8uegucku5j9mztjmr4sx2
6276986
6276982
2026-07-23T22:35:09Z
Wikihez
7344
6276986
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = 1, footer=nil, nocat = args.nocat, noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''") or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
9lmghfqflv549acrtteyeusnd7a7euv
6276987
6276986
2026-07-23T22:44:33Z
Wikihez
7344
6276987
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = false, nocat = args.nocat, noreq = args.noreq,
}
return '<span class="jegirtin-text">' .. formatted .. "</span>" .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''") or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. '<span class="jeder" style="font-size: 0.85em"> — (' .. source_text .. ")</span>"
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
7if1xaxf4z8uegucku5j9mztjmr4sx2
6276995
6276987
2026-07-23T23:24:28Z
Wikihez
7344
6276995
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode, source_text)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = false, nocat = args.nocat, noreq = args.noreq,
source = source_text,
}
return formatted .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''") or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode, source_text) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
48omxyovnftbvim9hkhm856t6j83uav
6277014
6276995
2026-07-24T00:35:50Z
Wikihez
7344
6277014
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode, source_text)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = false, nocat = args.nocat, noreq = args.noreq,
source = source_text,
}
return formatted .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(j_pewist(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''")) or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode, source_text) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
bgfcuru9zdjm0f08nakqaqbw1s9usl1
6277015
6277014
2026-07-24T00:39:02Z
Wikihez
7344
6277015
Scribunto
text/plain
--[=[
Citation system for ku.wiktionary — {{jêder-kitêb}}, {{jêder-kovar}}, {{jêder-malper}}.
({{jêder}} itself is left unchanged and is NOT handled by this module any more.)
KEY IDEAS
---------
* English parameter names (the ones [[Module:quote]] uses on en.wiktionary) are CANONICAL; the old
Kurdish names are aliases. So an English quotation template pastes in almost verbatim, while existing
Kurdish entries keep working.
* TYPE-DEPENDENT ALIASES (ported from Module:quote's `type_alias_specs`). Some friendly names mean
different things depending on the citation type. The most important case: in a JOURNAL, |title=/
|article= is the quoted piece (canonical `chapter`) and |journal=/|magazine=/|work= is the italic
container (canonical `title`); in a BOOK, |title= is the italic book itself. This is handled by
`type_alias_specs` + `process_type_aliases()` below, which run BEFORE [[Module:parameters]]: they copy
a friendly alias into its canonical parameter (and error if two aliases of the same thing are both
given). This is exactly how en.wikt lets one engine serve quote-book / quote-journal / quote-av.
* CANONICAL MODEL (same as en.wikt): `chapter` = the "quoted" sub-piece (article / episode / web page),
`title` = the ''italic'' container work (book / journal / website / show). The formatter renders
“chapter”, di ''title'' de — in en.wikt bibliographic ORDER and STYLE, with Kurdish connector WORDS
(r., cild, hejmar, çap, di … de).
* Argument handling uses [[Module:parameters]] with return_unknown = true, so an undeclared parameter
becomes a red error + maintenance category (UNKNOWN_PARAM_CATEGORY) instead of being ignored.
* The quoted-text portion (|text=/|jêgirtin=, |tr=, |t=/|werger= …) is delegated to
[[Module:usex/ceribandin]] (same as [[Module:quote]]).
Adding a plain parameter is one line in `params`; adding a type-specific friendly name is one line in
`type_alias_specs`.
SCOPE: this reproduces en.wikt's LAYOUT and its type-alias mechanism, not the full 3,500-line engine.
The common bibliographic fields + identifiers render in en.wikt style; a few exotic Module:quote params
are accepted (so pasted templates don't error) but not individually rendered — add them to
format_source() as needed.
]=]
local export = {}
------------------------------------------------------------------
-- Module names
------------------------------------------------------------------
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local scripts_module = "Module:scripts"
local usex_module = "Module:usex/ceribandin"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end
local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end
local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end
local function process_params(...) process_params = require(parameters_module).process return process_params(...) end
local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end
local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end
local function track(page) debug_track("jêder/" .. page) end
-- if-not-empty
local function ine(v)
if v == nil then return nil end
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then return nil end
end
return v
end
------------------------------------------------------------------
-- Declared (canonical) parameters + type-INDEPENDENT aliases.
-- Type-DEPENDENT friendly names (title/article/journal/magazine/sernav/kovar/malper/episode/season/…)
-- live in `type_alias_specs`, not here.
------------------------------------------------------------------
local boolean = {type = "boolean"}
local list = {list = true}
local params = {
-- language
[1] = true,
["lang"] = true,
-- quoted text
["text"] = true,
["passage"] = {alias_of = "text"},
["jêgirtin"] = {alias_of = "text"},
["jg"] = {alias_of = "text"},
["tr"] = true, ["transliteration"] = {alias_of = "tr"},
["ts"] = true, ["transcription"] = {alias_of = "ts"},
["t"] = true,
["translation"] = {alias_of = "t"}, ["gloss"] = {alias_of = "t"},
["werger"] = {alias_of = "t"}, ["w"] = {alias_of = "t"}, ["tercime"] = {alias_of = "t"},
["norm"] = true, ["subst"] = true, ["lit"] = true, ["lb"] = true, ["footer"] = true,
["brackets"] = boolean, ["termlang"] = true, ["noreq"] = boolean,
["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"},
["origtext"] = true, ["origtr"] = true, ["origts"] = true, ["orignorm"] = true,
["origsc"] = true, ["origlb"] = true, ["origlang"] = true, ["sc"] = true,
-- people
["author"] = list,
-- alias of a list param must itself be list (Module:parameters rule)
["nivîskar"] = {alias_of = "author", list = true},
["authorlink"] = list, ["last"] = list, ["first"] = list,
["2ndauthor"] = true, ["coauthors"] = true, ["mainauthor"] = true,
["editor"] = true, ["editors"] = true, ["amadekar"] = {alias_of = "editor"},
["tlr"] = true,
["translator"] = {alias_of = "tlr"}, ["translators"] = {alias_of = "tlr"},
["wergêr"] = {alias_of = "tlr"}, ["terciman"] = {alias_of = "tlr"},
["compiler"] = true, ["compilers"] = true, ["director"] = true, ["directors"] = true,
["quotee"] = true, ["others"] = true, ["by"] = true,
["role"] = true, ["speaker"] = {alias_of = "role"}, ["actor"] = true, ["artist"] = true,
["composer"] = true, ["lyricist"] = true,
-- title / container (canonical) — friendly names are type-dependent (see type_alias_specs)
["title"] = true,
["trans-title"] = true,
["sernavê orjînal"] = true, -- legacy Kurdish "original title", rendered in brackets
["chapter"] = true, ["trans-chapter"] = true, ["chapterurl"] = true,
["chapter_plain"] = true, ["chapter_number"] = true,
["entry"] = true, ["entryurl"] = true, ["trans-entry"] = true,
["section"] = true, ["sectionurl"] = true,
["series"] = true, ["seriesvolume"] = true,
["notitle"] = boolean,
-- edition / numbering
["edition"] = true, ["çap"] = {alias_of = "edition"}, ["edition_plain"] = true,
["volume"] = true, ["vol"] = {alias_of = "volume"},
["cild"] = {alias_of = "volume"}, ["cîld"] = {alias_of = "volume"},
["volume_plain"] = true, ["volumes"] = true,
["issue"] = true, ["hejmar"] = {alias_of = "issue"}, ["hj"] = {alias_of = "issue"},
["number"] = true,
["page"] = true, ["rûpel"] = {alias_of = "page"}, ["r"] = {alias_of = "page"},
["pages"] = true, ["pageurl"] = true, ["column"] = true, ["line"] = true, ["lines"] = true,
-- publication facts
["location"] = true, ["cih"] = {alias_of = "location"},
["publisher"] = true, ["weşanxane"] = {alias_of = "publisher"},
["year"] = true, ["sal"] = {alias_of = "year"},
["date"] = true, ["dîrok"] = {alias_of = "date"}, ["tarîx"] = {alias_of = "date"},
["month"] = true,
["year_published"] = true, ["sala weşandinê"] = {alias_of = "year_published"},
["start_date"] = true, ["start_year"] = true, ["origdate"] = true, ["origyear"] = true,
["accessdate"] = true, ["archivedate"] = true, ["nodate"] = boolean,
["type"] = true, ["genre"] = true, ["format"] = true, ["medium"] = true,
["platform"] = true, ["system"] = true, ["source"] = true,
-- URLs
["url"] = true, ["urls"] = true,
["archiveurl"] = true, ["arşîv"] = {alias_of = "archiveurl"},
-- identifiers
["isbn"] = true, ["ISBN"] = {alias_of = "isbn"},
["issn"] = true, ["ISSN"] = {alias_of = "issn"},
["oclc"] = true, ["OCLC"] = {alias_of = "oclc"},
["doi"] = true, ["DOI"] = {alias_of = "doi"},
["jstor"] = true, ["JSTOR"] = {alias_of = "jstor"},
["lccn"] = true, ["LCCN"] = {alias_of = "lccn"},
["pmid"] = true, ["PMID"] = {alias_of = "pmid"},
["pmcid"] = true, ["PMC"] = {alias_of = "pmcid"},
["bibcode"] = true,
["ol"] = true, ["OL"] = {alias_of = "ol"},
["ssrn"] = true, ["SSRN"] = {alias_of = "ssrn"},
["urn"] = true, ["URN"] = {alias_of = "urn"},
["id"] = true,
}
local UNKNOWN_PARAM_CATEGORY = "Rûpelên bi parametreyên nenas ên jêder"
local MISSING_CAT = "Şablon:jêder parametre duyem kêm e"
------------------------------------------------------------------
-- type_alias_specs (ported from [[Module:quote]] and localised for ku.wikt)
------------------------------------------------------------------
-- Each entry maps a citation TYPE to a list of {canon, "comma,separated,aliases"} pairs. When the citation
-- is of that type, each alias found in the arguments is copied into `canon` (and the alias wiped); giving
-- two aliases of the same canon, or an alias plus the canon, is an error. Numeric aliases are positional
-- parameters (they shift down by one when |lang= is used instead of |1=). Because the specs are processed
-- in order, "title -> chapter" can run before "journal -> title", which is what lets |title= mean the
-- article in a journal while |journal= becomes the italic container.
--
-- Add a new friendly name by dropping it into the relevant comma-list; add a whole new type by adding a key.
local type_alias_specs = {
book = {
{"year", "2"},
{"author", "3"},
{"title", "sernav,4"},
{"url", "5"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
{"chapter", "beş"},
{"trans-title", "tercimeya sernav,wergera sernav"},
},
journal = {
{"year", "2"},
{"author", "3"},
-- the article (quoted) -> canonical `chapter`
{"chapter", "title,article,gotar,sernav,4"},
{"chapterurl", "titleurl,articleurl"},
{"trans-chapter", "trans-title,trans-article,tercimeya sernav,wergera sernav"},
-- the periodical (italic container) -> canonical `title`
{"title", "journal,magazine,newspaper,work,kovar,rojname,5"},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work"},
{"tlr", "journal_translator,journal_translators,magazine_translator,work_translator"},
{"url", "6,journalurl,magazineurl,workurl"},
{"page", "7"},
{"source", "newsagency,news_agency,ajans"},
{"text", "8"},
{"t", "9"},
},
web = {
{"year", "2"},
{"author", "3"},
-- the page (quoted) -> canonical `chapter`
{"chapter", "title,sernav,4"},
{"chapterurl", "titleurl"},
{"trans-chapter", "trans-title,tercimeya sernav,wergera sernav"},
-- the site (container) -> canonical `title`
{"title", "work,site,website,malper,websîte,5"},
{"trans-title", "trans-work,trans-site"},
},
av = {
{"author", "writer,writers,nivîskar"},
-- only the episode is the "quoted" piece; the show/film |title= stays the italic work
{"chapter", "episode,beş"},
{"chapterurl", "episodeurl"},
{"trans-chapter", "trans-episode"},
{"title", "work,series,rêze,sernav"},
{"volume", "season,sezon"},
{"volume_plain", "season_plain"},
{"platform", "network,kanal"},
},
}
-- Copy type-specific friendly aliases into their canonical parameters, in place, before Module:parameters.
local function process_type_aliases(args, typ)
local specs = type_alias_specs[typ]
if not specs then return end
local deprecated = ine(args.lang) ~= nil -- |lang= used instead of |1= -> positional shift
for _, spec in ipairs(specs) do
local canon = spec[1]
local saw_key, saw_val
for alias in spec[2]:gmatch("[^,]+") do
local key = alias
if alias:match("^%d+$") then
local n = tonumber(alias)
if deprecated then n = n - 1 end
key = n
end
local v = ine(args[key])
if v ~= nil then
if saw_key == nil then
saw_key, saw_val = key, v
else
error(("|%s= û |%s= aliasên hev in; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), tostring(key)))
end
end
end
if saw_key ~= nil then
if ine(args[canon]) ~= nil and canon ~= saw_key then
error(("|%s= aliasê |%s= ye; nabe ku herdu bi hev re werin dayîn")
:format(tostring(saw_key), canon))
end
args[canon] = saw_val
if canon ~= saw_key then args[saw_key] = nil end
end
end
end
------------------------------------------------------------------
-- Small helpers
------------------------------------------------------------------
local function j_pewist(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end
local function err(text) return '<span class="error" style="color:red; font-size: 0.85em; display: inline;">\'\'\'' .. text .. "'''</span>" end
local function join_and(t)
if not t or #t == 0 then return nil end
if #t == 1 then return t[1] end
if #t == 2 then return t[1] .. " û " .. t[2] end
return concat(t, ", ", 1, #t - 1) .. " û " .. t[#t]
end
local function bold_year(s)
if s:find("'''") then return s end
return (s:gsub("(%d%d%d%d)", "'''%1'''", 1))
end
local function gadget_marker(langcode)
return '<span class="example"><bdi lang="' .. (langcode or "") .. '" style="display: none"></bdi></span>'
end
local function get_langobj(langcode, categories)
if not langcode then insert(categories, MISSING_CAT) return nil end
local ok, lang = pcall(get_lang, langcode)
if ok and lang then return lang end
insert(categories, MISSING_CAT)
return nil
end
local function linked(text, url)
if url then return text .. "<sup>[" .. url .. "]</sup>" end
return text
end
------------------------------------------------------------------
-- Identifier links (en.wikt style arrows)
------------------------------------------------------------------
local id_order = {
{"isbn", function(v) return "[[Special:BookSources/" .. v .. "|→ISBN]]" end},
{"issn", function(v) return "[https://www.worldcat.org/issn/" .. v .. " →ISSN]" end},
{"oclc", function(v) return "[https://www.worldcat.org/oclc/" .. v .. " →OCLC]" end},
{"doi", function(v) return "[https://doi.org/" .. mw.uri.encode(v, "PATH") .. " →DOI]" end},
{"jstor", function(v) return "[https://www.jstor.org/stable/" .. v .. " →JSTOR]" end},
{"lccn", function(v) return "[https://lccn.loc.gov/" .. v .. " →LCCN]" end},
{"pmid", function(v) return "[https://www.ncbi.nlm.nih.gov/pubmed/" .. v .. " →PMID]" end},
{"pmcid", function(v) return "[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. v .. " →PMC]" end},
{"bibcode", function(v) return "[https://ui.adsabs.harvard.edu/abs/" .. v .. " →bibcode]" end},
{"ol", function(v) return "[https://openlibrary.org/" .. v .. " →OL]" end},
{"ssrn", function(v) return "[https://ssrn.com/abstract=" .. v .. " →SSRN]" end},
}
------------------------------------------------------------------
-- Quoted-text portion
------------------------------------------------------------------
local function build_quote(args, lang, langcode, source_text)
local text = args.text
if not text then return nil end
local sc = args.sc and get_script(args.sc) or nil
local formatted = format_usex{
lang = lang,
termlang = args.termlang and get_lang(args.termlang) or lang,
usex = text, sc = sc,
transliteration = args.tr, transcription = args.ts,
translation = args.t, normalization = args.norm,
subst = args.subst, lit = args.lit, brackets = args.brackets,
quote = "quote-meta", inline = false, nocat = args.nocat, noreq = args.noreq,
source = source_text,
}
return formatted .. gadget_marker(langcode)
end
------------------------------------------------------------------
-- Source line, en.wikt bibliographic order (canonical: chapter = quoted piece, title = italic container)
------------------------------------------------------------------
local function format_source(args, typ, categories)
local parts = {}
local function add(x) if x then insert(parts, x) end end
-- 1. date / year (bold year)
add(j_pewist(args.date and bold_year(args.date) or (args.year and "'''" .. args.year .. "'''")) or nil)
-- 2. people
add(j_pewist(join_and(args.author)))
if not args.author or #args.author == 0 then
if args.last and #args.last > 0 then
local names = {}
for i, l in ipairs(args.last) do
local f = args.first and args.first[i]
names[i] = f and (l .. ", " .. f) or l
end
add(j_pewist(join_and(names)))
end
end
add(args.mainauthor and j_pewist(args.mainauthor) or nil)
add((args.editor or args.editors) and j_pewist("edîtor: " .. (args.editor or args.editors)) or nil)
add(args.tlr and ("(" .. j_pewist("terciman: " .. args.tlr) .. ")") or nil)
add(args.quotee and ("neqil ji " .. args.quotee) or nil)
add(args.others)
-- 3. chapter (quoted piece) + title (italic container), styled per type
local chapter = args.chapter or args.entry or args.section
local chapterurl = args.chapterurl or args.entryurl or args.sectionurl
local title = args.title
if typ == "journal" then
if chapter then
add(linked("“" .. chapter .. "”", args.url or chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("di ''" .. title .. "'' de")
add(args["trans-title"] and ("[''" .. args["trans-title"] .. "'']") or nil)
else
add(err("navê kovarê kêm e")); insert(categories, MISSING_CAT)
end
elseif typ == "web" then
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
end
if title then
add("li ser " .. title)
elseif not chapter then
add(err("navê malperê kêm e")); insert(categories, MISSING_CAT)
end
else -- book / av
if chapter then
add(linked("“" .. chapter .. "”", chapterurl))
add(args["trans-chapter"] and ("[“" .. args["trans-chapter"] .. "”]") or nil)
if title then
add(linked("di ''" .. title .. "'' de", args.url))
end
elseif title then
add(linked("''" .. title .. "''", args.url))
add(args["trans-title"] and ("[\"" .. args["trans-title"] .. "\"]") or nil)
add(args["sernavê orjînal"] and ("[\"" .. args["sernavê orjînal"] .. "\"]") or nil)
else
add(err("sernavê pirtûkê kêm e")); insert(categories, MISSING_CAT)
end
end
-- 4. series
if args.series then
add(args.seriesvolume and (args.series .. " " .. args.seriesvolume) or args.series)
end
-- 5. edition
if args.edition then
add("çapa " .. args.edition .. (args.edition == "1" and "ê" or "an"))
end
add(args.edition_plain)
-- 6. volume / issue / number
add(args.volume and ("cild " .. args.volume) or nil)
add(args.issue and ("hejmar " .. args.issue) or (args.number and ("hejmar " .. args.number)) or nil)
-- 7. location: publisher
if args.location and args.publisher then
add(args.location .. ": " .. args.publisher)
else
add(args.location); add(args.publisher)
end
add(args.year_published and ("sala weşandinê: " .. args.year_published) or nil)
-- 8. page(s) / column / line
if args.pages then add("rr. " .. args.pages)
elseif args.page then add("r. " .. args.page) end
add(args.column and ("stûn " .. args.column) or nil)
add((args.lines and ("rêz " .. args.lines)) or (args.line and ("rêz " .. args.line)) or nil)
-- 9. type / genre / format / medium / platform
add(args.genre); add(args.medium); add(args.format); add(args.platform)
-- 10. identifiers (fixed order)
for _, spec in ipairs(id_order) do
local v = args[spec[1]]
if v then add(spec[2](v)) end
end
add(args.id)
-- 11. web link + archive (for web the container has no url; show the link here)
if typ == "web" and args.url then
local link = " →<span class=\"plainlinks\"> [" .. args.url .. " lînk]</span>"
if args.archiveurl then
link = link .. " [<span class=\"plainlinks\">[" .. args.archiveurl .. " arşîv]</span>]"
else
link = link .. " [<span class=\"plainlinks\">[https://web.archive.org/" .. args.url .. " arşîv]</span>]"
end
add(link)
end
add(args.accessdate and ("gihîştin " .. args.accessdate) or nil)
return concat(parts, ", ")
end
local kind_category_word = {
book = "kitêban",
journal = "kovaran",
web = "malperan",
av = "deng-dîmenan",
}
------------------------------------------------------------------
-- Shared rendering
------------------------------------------------------------------
function export.render(raw_args, default_type)
-- mutable copy of the (read-only) parent args
local a = {}
for k, v in pairs(raw_args) do a[k] = v end
-- resolve citation type: explicit |type= wins, else the template's default; unknown -> default
local typ = ine(a.type) or default_type
if not type_alias_specs[typ] then typ = default_type end
a.type = nil
-- apply type-specific friendly aliases, then validate everything
process_type_aliases(a, typ)
local args, unknown = process_params(a, params, true)
local categories = {}
local langcode = args[1] or args.lang
local lang = get_langobj(langcode, categories)
-- unknown parameters -> red error + maintenance category
local unknown_error = ""
if unknown then
local bad = {}
for name in pairs(unknown) do insert(bad, tostring(name)) end
if #bad > 0 then
table.sort(bad)
unknown_error = " " .. err("parametreya(yên) nenas: " .. concat(bad, ", "))
insert(categories, UNKNOWN_PARAM_CATEGORY)
end
end
local source_text = format_source(args, typ, categories)
local quote_text = build_quote(args, lang, langcode, source_text) or ""
local cat_text = ""
if not args.nocat then
local cats = {}
if lang then
local langname = lang:getCanonicalName()
insert(cats, "Jêgirtin bi " .. langname)
local word = kind_category_word[typ]
if word then insert(cats, "Jêgirtinên " .. word .. " bi " .. langname) end
end
for _, c in ipairs(categories) do insert(cats, c) end
if #cats > 0 then
cat_text = format_categories(cats, lang or get_langobj("und", {}))
end
end
return quote_text
.. unknown_error
.. cat_text
end
------------------------------------------------------------------
-- Template entry points (Template:jêder is intentionally NOT handled here)
------------------------------------------------------------------
function export.kiteb_t(frame) return export.render(frame:getParent().args, "book") end
function export.kovar_t(frame) return export.render(frame:getParent().args, "journal") end
function export.malper_t(frame) return export.render(frame:getParent().args, "web") end
return export
7nyzw228ahd0597e6mjqhllk7jwj0td
ẍezete
0
1358152
6276969
2026-07-23T20:18:11Z
Wikihez
7344
([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +ku:navdêr:
6276969
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Navdêr ===
{{ku-tewîn-nav|ẍezete|mê}}
{{navdêr|ku|c=m}}
# {{guharto|ku|xezete}}
#* {{bênimûne|ku}}
897cq83uwliapecryrlrnum083fmfg6
Modul:italics
828
1358153
6277034
2026-07-24T01:32:02Z
Wikihez
7344
[[:en:Module:italics]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]])
6277034
Scribunto
text/plain
local export = {}
local m_string_utils = require("Module:string utilities")
local find = m_string_utils.find
local match = m_string_utils.match
local gsub = m_string_utils.gsub
function export.i(text)
if text == "" or text == nil then
return nil
end
if type(text) == "table" and text.args then
text = text.args[1]
end
-- Remove whitespace from beginning and end of text.
text = mw.text.trim(text)
-- Find parenthesized text.
local parenthesis = ""
if find(text, "%b()$") then
text, parenthesis = match(text, "^(.*)(%b())$")
if text == "" or text == nil then
error("Malformed page name: " .. text)
end
end
text = "''" .. text .. "''"
--[[ Adds italics toggle ('') around the whitespace
that surrounds various things that aren't supposed to be italicized:
for instance, Fragaria × ananassa becomes ''Fragaria'' × ''ananassa''.
(The hybridization symbol × isn't supposed to be italicized.) ]]
local notItalicized = {
["subsp."] = true, ["ssp."] = true, ["var."] = true, ["f."] = true,
["sect."] = true, ["subsect."] = true, ["subg."] = true, ["infrasp."] = true,
["ser."] = true, ["subser."] = true,
["series"] = true, ["subseries"] = true,
["forma"] = true, ["form"] = true, ["morph"] = true, ["morpha"] = true
}
local hybrid = "×"
text = text:gsub("(%s*([a-z]+%.)%s*)",
function(wholeMatch, abbreviation)
if notItalicized[abbreviation] then
return "''" .. wholeMatch .. "''"
end
end)
text = text:gsub("%s*" .. hybrid .. "%s*", "''%0''"):gsub("%f[']''''%f[^']", "")
return text .. parenthesis
end
function export.unitalicize_brackets(text)
if type(text) == "table" and text.args then
text = text.args[1]
end
if not text or text == "" then
return nil
end
local function unitalicize(text)
return '<span style="font-style: normal;">' .. text .. '</span>'
end
local function process(text)
if text:find("[[", 1, true) then
if text:find("|") then
return text:gsub(
"|.-%]%]",
function (piping)
return piping:gsub("%b[]", process)
end)
end
-- do nothing with un-piped wikilinks
--[=[
elseif text:find("[http", 1, true) then
return text:gsub(
"%[([^ ]+ )([^%]]+)%]",
function (URL, link_text)
return "[" .. URL .. process(link_text) .. "]"
end)
--]=]
elseif text:find("^%[https?://") then
return text:gsub(
" .+",
function (link_text)
return link_text:gsub("%b[]", process)
end)
else
local inside_brackets = text:sub(2, -2)
if inside_brackets == "..." or inside_brackets == "…" then
return unitalicize(text)
else
return unitalicize("[") .. inside_brackets .. unitalicize("]")
end
end
end
text = text:gsub("%b[]", process)
return text
end
function export.test(frame)
local text = frame.args[1]
local quote = require("Module:yesno")(frame.args.quote)
if quote then
return export.unitalicize_brackets(text)
else
return export.i(text)
end
end
return export
gqaycgs6fu5x60yjqb8bgxh8n6y1zvq
Modul:italics/belge
828
1358154
6277035
2026-07-24T01:32:03Z
Wikihez
7344
[[:en:Module:italics/documentation]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]])
6277035
wikitext
text/x-wiki
This module applies proper italicization to the taxonomic name of a genus, species, subspecies, variety, or form; or italicizes a title while not italicizing a parenthetical disambiguator. It is intended to be used in interwiki link modules, but might be useful elsewhere too.
==Testcases==
<pre>
{{#invoke:italics|test|<cite>A Voyage around the World. [...] In Two Volumes</cite>|quote=1}}
{{#invoke:italics|test|<cite>A Response to a Scurrilous Libel by J[onathan] S[wift]. [...] In Two Volumes</cite>|quote=1}}
{{#invoke:italics|test|<cite>[[w:Essays (Montaigne)|The Essayes, or, Morall, Politike and Millitarie Discourses of Lo. Michaell de Montaigne, Knight of the Noble Order of St. Michaell, and One of the Gentlemen in Ordinary of the French King, Henry the Third His Chamber]]</cite>|quote=1}}
{{#invoke:italics|test|<cite>[[w:Cyclopædia, or an Universal Dictionary of Arts and Sciences|Cyclopædia: Or, An Universal Dictionary of Arts and Sciences; [...] In Two Volumes]]</cite>|quote=1}}
{{#invoke:italics|test|1=[http://dsalsrv02.uchicago.edu/cgi-bin/philologic/search3advanced?dbname=mcgregor&query=R:hi:McGregor&searchdomain=headwords&matchtype=exact&display=utf8 {{lang|hi|R:hi:McGregor}}]|quote=1}}
{{#invoke:italics|test|Pinus contorta subsp. latifolia}}
{{#invoke:italics|test|Cupressus arizonica var. glabra}}
{{#invoke:italics|test|Fragaria vesca subsp. vesca f. semperflorens}}
{{#invoke:italics|test|Fragaria × ananassa}}
{{#invoke:italics|test|Argentina (plant)}}
{{#invoke:italics|test|× Agroelymus}} <!-- This now passess 20181225-19:12 -->
{{#invoke:italics|test|A sample item containing a continuation, &c {{...}} <!-- This fails currently --> }}
{{#invoke:italics|test|A sample item containing a continuation, &c {{nb...}} <!-- This fails currently --> }}
{{#invoke:italics|test|[[Wiktionary:Main_Page|A sample item containing a continuation, &c {{...}}]] <!-- This fails currently --> }}
{{#invoke:italics|test|[https://en.wiktionary.org/wiki/Wiktionary:Main_Page A sample item containing a continuation, [&c {{...}}]]<!-- This fails currently, Generates a missing span... -->}}
{{#invoke:italics|test|'''Polygonum''' aviculare}}
</pre>
* {{#invoke:italics|test|<cite>A Voyage around the World. [...] In Two Volumes</cite>|quote=1}}
* {{#invoke:italics|test|<cite>A Response to a Scurrilous Libel by J[onathan] S[wift]. [...] In Two Volumes</cite>|quote=1}}
* {{#invoke:italics|test|<cite>[[w:Essays (Montaigne)|The Essayes, or, Morall, Politike and Millitarie Discourses of Lo. Michaell de Montaigne, Knight of the Noble Order of St. Michaell, and One of the Gentlemen in Ordinary of the French King, Henry the Third His Chamber]]</cite>|quote=1}}
* {{#invoke:italics|test|<cite>[[w:Cyclopædia, or an Universal Dictionary of Arts and Sciences|Cyclopædia: Or, An Universal Dictionary of Arts and Sciences; [...] In Two Volumes]]</cite>|quote=1}}
* {{#invoke:italics|test|1=[http://dsalsrv02.uchicago.edu/cgi-bin/philologic/search3advanced?dbname=mcgregor&query=R:hi:McGregor&searchdomain=headwords&matchtype=exact&display=utf8 {{lang|hi|R:hi:McGregor}}]|quote=1}}
* {{#invoke:italics|test|Pinus contorta subsp. latifolia}}
* {{#invoke:italics|test|Cupressus arizonica var. glabra}}
* {{#invoke:italics|test|Fragaria vesca subsp. vesca f. semperflorens}}
* {{#invoke:italics|test|Fragaria × ananassa}}
* {{#invoke:italics|test|Argentina (plant)}}
* {{#invoke:italics|test|× Agroelymus}}
* {{#invoke:italics|test|A sample item containing a continuation, &c {{...}} }}
* {{#invoke:italics|test|A sample item containing a continuation, &c {{nb...}} }}
* {{#invoke:italics|test|[[Wiktionary:Main_Page|A sample item containing a continuation, &c {{...}}]]}}
* {{#invoke:italics|test|[https://en.wiktionary.org/wiki/Wiktionary:Main_Page A sample item containing a continuation, [&c {{...}}]]}}
* {{#invoke:italics|test|'''Polygonum''' aviculare}}
{{temp|taxlink|Rosa × alba|nothospecies}} - {{taxlink|Rosa × alba|nothospecies}}
{{module cat|-|Utility}}
6ehuy3opfo8pl9aztno0ppf2cl6ve4k
Modul:roman numerals
828
1358156
6277036
2026-07-24T01:32:48Z
Wikihez
7344
[[:en:Module:roman numerals]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]])
6277036
Scribunto
text/plain
local roman_dict = {M = 1000, D = 500, C = 100, L = 50, X = 10, V = 5, I = 1}
local repeatable = {M = true, C = true, X = true, I = true}
local roman_table = {'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'}
local arabic_table = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }
local export = {}
local function nil_or_error(msg, no_error)
if no_error then
return nil
else
error(msg, 2)
end
end
function export.arabic_to_roman(arabic, no_error, to_lower, use_j, use_iiii)
if arabic == '' or arabic == nil then return nil end
arabic = mw.ustring.gsub(arabic, ",", "")
local j = tonumber(arabic)
if j == nil then return nil end
if j <= 0 or j > 3999 then
return nil_or_error("Out of valid range", no_error)
end
local result = {}
local a, count
for i, r in ipairs(roman_table) do
a = arabic_table[i]
count = math.floor(j / a)
table.insert(result, string.rep(r, count))
j = j % a
if j <= 0 then break end
end
local out = table.concat(result)
if to_lower then out = mw.ustring.lower(out) end
if use_iiii and out:sub(-2, -1) == "IV" then out = out:sub(1, -3) .. "IIII" end
if use_iiii and out:sub(-2, -1) == "iv" then out = out:sub(1, -3) .. "iiii" end
if use_j and out:sub(-1, -1) == "I" then out = out:sub(1, -2) .. "J" end
if use_j and out:sub(-1, -1) == "i" then out = out:sub(1, -2) .. "j" end
return out
end
function export.roman_to_arabic(roman, no_error)
if roman == '' or roman == nil then
return nil
else
roman = mw.ustring.upper(roman):gsub("J", "I")
end
if not mw.ustring.match(roman, "^[MDCLXVI]+$") then
return nil_or_error("Illegal Roman numeral format", no_error)
end
local result = 0
local i = 1
local s1, s2, c2
local length = #roman
while i <= length do
s1 = roman_dict[roman:sub(i, i)]
if s1 == nil then
return nil_or_error("Unrecognized character in input", no_error)
elseif (i + 1) <= length then
c2 = roman:sub(i + 1, i + 1)
s2 = roman_dict[c2]
if s2 == nil then
return nil_or_error("Unrecognized character in input", no_error)
elseif s1 >= s2 then
if s1 == s2 and not repeatable[c2] then
return nil_or_error("Illegal Roman numeral format: “" .. c2 .. "” may not appear adjacent to itself", no_error)
end
result = result + s1
else
result = result + s2 - s1
i = i + 1
end
else
result = result + s1
end
i = i + 1
end
return result
end
-- implements {{R2A}}
function export.roman_to_arabic_t(frame)
local params = {
[1] = {},
['no_error'] = {type = 'boolean'},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
return export.roman_to_arabic(args[1], args.no_error)
end
-- implements {{A2R}}
function export.arabic_to_roman_t(frame)
local params = {
[1] = {},
['lower'] = {type = 'boolean'},
['j'] = {type = 'boolean'},
['iiii'] = {type = 'boolean'},
['no_error'] = {type = 'boolean'},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
return export.arabic_to_roman(args[1], args.no_error, args.lower, args.j, args.iiii)
end
return export
5ekwz59wr6lrq4itdhy0d5217t4sq2s
Modul:roman numerals/belge
828
1358157
6277037
2026-07-24T01:32:48Z
Wikihez
7344
[[:en:Module:roman numerals/documentation]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]])
6277037
wikitext
text/x-wiki
: ''See [[Template:A2R]] and [[Template:R2A]].''
{{#invoke:roman numerals/testcases|run_tests}}<!-- Replace this with a short description of the purpose of the module, and how to use it. -->
<includeonly>
[[Category:Number modules]]
</includeonly>
ebdicoj6b02nd1peh2gmag6cpan4j05
Modul:roman numerals/testcases
828
1358159
6277050
2026-07-24T02:15:30Z
Wikihez
7344
[[:en:Module:roman numerals/testcases]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]])
6277050
Scribunto
text/plain
local tests = require('Module:UnitTests')
local m_roman = require('Module:roman_numerals')
function tests:check_roman_to_arabic(roman, arabic)
self:equals(
roman,
m_roman.roman_to_arabic(roman), arabic
)
end
function tests:check_arabic_to_roman(arabic, roman)
self:equals(
arabic,
m_roman.arabic_to_roman(arabic), roman
)
end
function tests:test_arabic_to_roman()
self:check_arabic_to_roman(1, "I")
self:check_arabic_to_roman(2, "II")
self:check_arabic_to_roman(3, "III")
self:check_arabic_to_roman(4, "IV")
self:check_arabic_to_roman(5, "V")
self:check_arabic_to_roman(6, "VI")
self:check_arabic_to_roman(7, "VII")
self:check_arabic_to_roman(8, "VIII")
self:check_arabic_to_roman(9, "IX")
self:check_arabic_to_roman(10, "X")
self:check_arabic_to_roman(944, "CMXLIV")
self:check_arabic_to_roman(2848, "MMDCCCXLVIII")
self:check_arabic_to_roman(3999, "MMMCMXCIX")
end
function tests:test_roman_to_arabic()
self:check_roman_to_arabic("I", 1)
self:check_roman_to_arabic("II", 2)
self:check_roman_to_arabic("III", 3)
self:check_roman_to_arabic("IV", 4)
self:check_roman_to_arabic("V", 5)
self:check_roman_to_arabic("VI", 6)
self:check_roman_to_arabic("VII", 7)
self:check_roman_to_arabic("VIII", 8)
self:check_roman_to_arabic("IX", 9)
self:check_roman_to_arabic("X", 10)
self:check_roman_to_arabic("MCCXLI", 1241)
self:check_roman_to_arabic("CMXCIX", 999)
self:check_roman_to_arabic("MMMCMXCIX", 3999)
end
return tests
szaaqi5q1x5gqxldxo4679t0f6p7zpi