Wîkîferheng kuwiktionary https://ku.wiktionary.org/wiki/Destp%C3%AAk MediaWiki 1.47.0-wmf.24 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 can 0 15002 6794068 6630299 2026-10-14T20:22:37Z Wikihez 7344 /* Etîmolojî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794068 wikitext text/x-wiki {{h-bnr|Can|càn|cân|cần|çan|çaň}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{kîte|ku|can}} === Navdêr === {{navdêr|ku|z=n}} # [[giyan]], [[ruh]], [[saxî]], [[rih]], [[jiyan]], [[jîn]], [[gan]] (2) ==== Ji wêjeya klasîk ==== * {{J:Melayê Cizîrî:Dîwana Melayê Cizîrî|jêgirtin=Da weqtê liqayê bi te hey bin di beqayê<br />Min neqdê dil û '''can''' di fenayê bi selem da<br />Yaqûtifiroşan di kef elmas-i şikestin}} ==== Tewandin ==== {{ku-tewîn-nav|nêr}} ==== Bi alfabeyên din ==== * {{ku-ar|جان}} * {{ku-kr|щан}} ==== Jê ==== {{kol3|ku|cure=Jê |bêcan |bêcanî |bêcanîtî |bêcantî |bican |bicanî |bicanîtî |bicantî |candar |candarî |cander |canderî |candêr |candêrî |caneber |canewer |canfîda |cangorî |canî }} ==== Etîmolojî ==== {{etîmon|ku|:deyn|fa:جان|dar=1}} Ji {{deyn|fa|ku|جان}}. ==== Bide ber ==== {{kol3|ku |an |ban |dan |gan |han |jan |kan |man |nan |pan |ran |şan |tan |van |wan |xan |yan |zan }} ==== Biwêj ==== * [[can dan]]: 1. [[mirin]] 2) xwe bo [[xatir]]ê tiştekê/î dan [[kuştin]] * [[can ji dest dan]]: [[mirin]], [[wefat]] kirin * [[can winda kirin]]: [[mirin]], [[wefat]] kirin ==== Werger ==== {{werger-ser}} * {{Z|ab}}: {{W-|ab|аԥсы}} * {{Z|ady}}: {{W-|ady|псэ}} * {{Z|af}}: {{W+|af|siel}} * {{Z|ak}}: {{W-|ak|kra}}, {{W-|ak|ɔkra}} * {{Z|sq}}: {{W+|sq|shpirt|n}}, {{W-|sq|avë|m}} * {{Z|de}}: {{W+|de|Seele|m}} * {{Z|alt}}: {{W-|alt|тын}}, {{W-|alt|јан}}, {{W-|alt|рух}} * {{Z|am}}: {{W-|am|ነፍስ}} * {{Z|arc}}: *: Îbranî: {{W-|arc|רוחא|m|tr=rūħā}} * {{Z|rup}}: {{W-|rup|suflit}}, {{W-|rup|suflet}} * {{Z|ast}}: {{W+|ast|alma|m}} * {{Z|av}}: {{W-|av|рухӏ}} * {{Z|ain}}: {{W-|ain|ラマ|tr=rama}} * {{Z|az}}: {{W+|az|ruh}}, {{W+|az|can}} * {{Z|eu}}: {{W+|eu|arima}}, {{W-|eu|gogo}} * {{Z|ba}}: {{W-|ba|йән}}, {{W-|ba|рух}} * {{Z|be}}: {{W-|be|душа́|m}} * {{Z|bn}}: {{W+|bn|আত্মা}}, {{W+|bn|রূহ}}, {{W+|bn|জান}} * {{Z|bcl}}: {{W-|bcl|kalag}} * {{Z|br}}: {{W+|br|ene|n}} * {{Z|bg}}: {{W+|bg|душа́|m}} * {{Z|my}}: {{W+|my|ဝိညာဉ်}} * {{Z|ccc}}: {{W-|ccc|sana'ne}} * {{Z|ce}}: {{W-|ce|са}} * {{Z|cs}}: {{W+|cs|duše|m}} * {{Z|chr}}: {{W-|chr|ᎠᏓᎾᏔ}} * {{Z|zh}}: *: {{Z|yue}}: {{W-|yue|靈魂|tr=ling4 wan4}}, {{W-|yue|魂魄|tr=wan4 paak3}} *: {{Z|cmn}}: {{W+|cmn|靈魂|tr=línghún}}, {{W+|cmn|魂魄|tr=húnpò}} *: {{Z|nan}}: {{W-|nan|靈魂|tr=lêng-hûn}}, {{W+|nan|魂魄|tr=hûn-phek}} * {{Z|chk}}: {{W-|chk|ngun}} * {{Z|dlm}}: {{W-|dlm|jamna|m}} * {{Z|da}}: {{W+|da|sjæl|g}} * {{Z|ovd}}: {{W-|ovd|själ|m}} * {{Z|id}}: {{W+|id|ruh}} * {{Z|ar}}: {{W+|ar|رُوح|n|m}}, {{W+|ar|نَفْس|m}} *: {{Z|arz}}: {{W-|arz|روح|m|tr=rūḥ}}, {{W-|arz|نفس|m|tr=nafs}} * {{Z|hy}}: {{W+|hy|հոգի}} * {{Z|myv}}: {{W-|myv|ойме}} * {{Z|eo}}: {{W+|eo|animo}} * {{Z|et}}: {{W+|et|hing}} * {{Z|fa}}: {{W+|fa|روح|tr=ruh}}, {{W+|fa|روان|tr=ravân}}, {{W+|fa|جان|tr=jân}}, {{W+|fa|نفس|tr=nafs}} * {{Z|fo}}: {{W-|fo|sál|m}} * {{Z|fi}}: {{W+|fi|sielu}}, {{W+|fi|henki}} * {{Z|fr}}: {{W+|fr|âme|m}} *: {{Z|fro}}: {{W-|fro|ame}} * {{Z|fur}}: {{W-|fur|anime|m}} {{W-|fur|ànime|m}} * {{Z|frr}}: {{W-|frr|Siil|g}} * {{Z|stq}}: {{W-|stq|Seele|m}} * {{Z|fy}}: {{W+|fy|siel|g}}, {{W+|fy|siele|g}} * {{Z|gd}}: {{W-|gd|anam|n}} * {{Z|gl}}: {{W+|gl|alma|m}}, {{W+|gl|ánima|m}} * {{Z|got}}: {{W-|got|𐍃𐌰𐌹𐍅𐌰𐌻𐌰|m}} * {{Z|gu}}: {{W+|gu|આત્મા}} * {{Z|ka}}: {{W+|ka|სული}} * {{Z|haw}}: {{W-|haw|ʻuhane}} * {{Z|hi}}: {{W+|hi|आत्मा|m}}, {{W+|hi|रूह|m}}, {{W+|hi|नफ्स|m}} * {{Z|nl}}: {{W+|nl|ziel|m}}, {{W+|nl|geest|n}} * {{Z|he}}: {{W+|he|נְשָׁמָה|m|tr=n'shamá}}, {{W+|he|נֶפֶשׁ|m|tr=néfesh}}, {{W+|he|רוּחַ|n|m|tr=rúakh}} * {{Z|io}}: {{W+|io|anmo}} * {{Z|ilo}}: {{W-|ilo|kararua}} * {{Z|enm}}: {{W-|enm|soule}} * {{Z|en}}: {{W+|en|soul}} * {{Z|izh}}: {{W-|izh|henki}} * {{Z|ia}}: {{W-|ia|anima}} * {{Z|ga}}: {{W+|ga|anam|n}} *: {{Z|sga}}: {{W-|sga|ainimm|m}} * {{Z|ist}}: {{W-|ist|anema|m}} * {{Z|it}}: {{W+|it|anima|m}} * {{Z|is}}: {{W+|is|sál|m}} * {{Z|ja}}: {{W+|ja|魂|tr=たましい, tamashii}}, {{W+|ja|霊|tr=れい, rei, たま, tama}}, {{W+|ja|魂魄|tr=こんぱく, konpaku}} * {{Z|kbd}}: {{W-|kbd|псэ}} * {{Z|xal}}: {{W-|xal|седкл}} * {{Z|kn}}: {{W+|kn|ಆತ್ಮ}} * {{Z|pam}}: {{W-|pam|kaladua}} * {{Z|csb}}: {{W-|csb|dësza|m}} * {{Z|ca}}: {{W+|ca|ànima|m}} * {{Z|qu}}: {{W-|qu|aya}}, {{W+|qu|nuna}} * {{Z|ky}}: {{W+|ky|жан}}, {{W+|ky|рух}}, {{W+|ky|дил}} * {{Z|si}}: {{W+|si|ආත්මය}} * {{Z|swb}}: {{W-|swb|roho|c9|c10}} * {{Z|ko}}: {{W+|ko|영혼}}, {{W-|ko|령혼}}, {{W+|ko|혼백}}, {{W+|ko|넋}}, {{W+|ko|얼}} * {{Z|kw}}: {{W-|kw|ena|n|m}}, {{W-|kw|enev|n}} * {{Z|pov}}: {{W-|pov|alma}} * {{Z|lad}}: {{W-|lad|alma|m}} * {{Z|lkt}}: {{W-|lkt|naǧí}} * {{Z|ltg}}: {{W-|ltg|dvēsele}} * {{Z|la}}: {{W+|la|anima|m}}, {{W+|la|animus|n}} * {{Z|lv}}: {{W+|lv|dvēsele|m}}, {{W+|lv|velis|n}} * {{Z|lo}}: {{W-|lo|ວິນຍານ}} * {{Z|lez}}: {{W-|lez|руьгь}} * {{Z|lt}}: {{W+|lt|siela|m}} * {{Z|lb}}: {{W+|lb|Séil|m}} * {{Z|mk}}: {{W-|mk|душа|m}} * {{Z|ml}}: {{W+|ml|ആത്മാവ്}} * {{Z|ms}}: {{W+|ms|roh}}, {{W+|ms|jiwa}}, {{W+|ms|nyawa}} * {{Z|mt}}: {{W-|mt|ruħ|n}} * {{Z|mnc}}: {{W-|mnc|ᡶᠠᠶᠠᠩᡤᠠ}} * {{Z|gv}}: {{W-|gv|annym|n}} * {{Z|mi}}: {{W-|mi|wairua}} * {{Z|mr}}: {{W-|mr|आत्मा}} * {{Z|mhr}}: {{W-|mhr|чон}} * {{Z|mzn}}: {{W-|mzn|جان}} * {{Z|hu}}: {{W+|hu|lélek}}, {{W+|hu|önvaló}} * {{Z|mdf}}: {{W-|mdf|вайме}} * {{Z|mn}}: *: Kirîlî: {{W+|mn|сүнс}} *: Mongolî: {{W-|mn|ᠰᠦ᠋ᠨ᠋ᠡᠰᠦ}} * {{Z|nv}}: {{W-|nv|iiʼ sizíinii}} * {{Z|ne}}: {{W+|ne|आत्मा}} * {{Z|nog}}: {{W-|nog|ян}} * {{Z|non}}: {{W-|non|sál|m}} * {{Z|no}}: *: {{Z|nb}}: {{W+|nb|sjel|n|m}} *: {{Z|nn}}: {{W-|nn|sjel|m}} * {{Z|oj}}: {{W-|oj|ojichaag}} * {{Z|ryu}}: {{W-|ryu|まぶい|tr=mabui}}, {{W-|ryu|魂|tr=たましー, tamashī}} * {{Z|oc}}: {{W+|oc|arma|m}}, {{W+|oc|anma|m}} * {{Z|or}}: {{W-|or|ଆତ୍ମା}} * {{Z|os}}: {{W-|os|уд}} * {{Z|ota}}: {{W-|ota|روح|tr=ruh}} * {{Z|ug}}: {{W-|ug|روھ}}, {{W-|ug|دىل}} * {{Z|uz}}: {{W+|uz|ruh}}, {{W+|uz|jon}}, {{W+|uz|dil}}, {{W+|uz|nafs}} * {{Z|pi}}: {{W-|pi|viññāṇa}} * {{Z|ps}}: {{W+|ps|روح|m|tr=roh}}, {{W+|ps|نفس|n|tr=nafs}} * {{Z|pl}}: {{W+|pl|dusza|m}} * {{Z|roa-opt}}: {{W-|roa-opt|alma}} * {{Z|pt}}: {{W+|pt|alma|m}} * {{Z|prg}}: {{W-|prg|dūsi|m}} * {{Z|kk}}: {{W+|kk|жан}}, {{W-|kk|рух}}, {{W+|kk|діл}} * {{Z|cop}}: {{W-|cop|ⲯⲩⲭⲏ|n|m}} * {{Z|rm}}: {{W-|rm|olma|m}} * {{Z|ro}}: {{W+|ro|suflet|nt}} * {{Z|ru}}: {{W+|ru|душа́|m}} * {{Z|rue}}: {{W-|rue|душа́|m}} * {{Z|str}}: {{W-|str|SELI}} * {{Z|se}}: {{W-|se|heagga}} * {{Z|sa}}: {{W+|sa|आत्मन्|tr=ātmán|n}}, {{W+|sa|त्मन्|tr=tmán|n}}, {{W+|sa|विज्ञान|nt}} * {{Z|sc}}: {{W-|sc|ànima|m}} * {{Z|ceb}}: {{W-|ceb|kalag}} * {{Z|srn}}: {{W-|srn|kra}} * {{Z|sh}}: *: Kirîlî: {{W-|sh|ду́ша|m}} *: Latînî: {{W+|sh|dúša|m}} * {{Z|syc}}: {{W-|syc|ܪܘܚܐ|m|tr=rūħā}} * {{Z|cu}}: *: Kirîlî: {{W-|cu|доуша|m}} * {{Z|orv}}: {{W-|orv|душа|m}} * {{Z|sk}}: {{W-|sk|duša|m}} * {{Z|sl}}: {{W+|sl|duša|m}} * {{Z|so}}: {{W-|so|naf}} * Sorbî: *: {{Z|dsb}}: {{W-|dsb|duša|m}} *: {{Z|hsb}}: {{W-|hsb|duša|m}} * {{Z|es}}: {{W+|es|alma|m}} * {{Z|sw}}: {{W+|sw|roho}}, {{W+|sw|nafsi|c9|c10}} * {{Z|sv}}: {{W+|sv|själ}} * {{Z|shn}}: {{W-|shn|ၶႂၼ်ငဝ်း}}, {{W-|shn|ဝိၺၢၼ်ႇ}} * {{Z|tab}}: {{W-|tab|рюгь}}, {{W-|tab|жан}} * {{Z|tg}}: {{W-|tg|рӯҳ}}, {{W-|tg|ҷон}}, {{W+|tg|дил}}, {{W-|tg|нафс}} * {{Z|tl}}: {{W-|tl|kalag}}, {{W+|tl|kaluluwa}} * {{Z|ta}}: {{W+|ta|ஆன்மா}} * {{Z|tsg}}: {{W-|tsg|nyawa}} * {{Z|th}}: {{W+|th|วิญญาณ}} * {{Z|te}}: {{W+|te|ఆత్మ}} * {{Z|tt}}: {{W+|tt|рух}}, {{W+|tt|өрәк}}, {{W+|tt|кот}}, {{W+|tt|җан}} * {{Z|tr}}: {{W+|tr|ruh}}, {{W+|tr|can}}, {{W+|tr|tin}} * {{Z|tk}}: {{W-|tk|ruh}}, {{W+|tk|jan}} * {{Z|bo}}: {{W-|bo|རྣམ་ཤེས}} * {{Z|xto}}: {{W-|xto|āñcäm}} * {{Z|txb}}: {{W-|txb|āñme}} * {{Z|uga}}: {{W-|uga|𐎐𐎔𐎌}} * {{Z|ur}}: {{W-|ur|روح|m|tr=rūh}}, {{W+|ur|آتما|m|tr=ātmā}} * {{Z|uk}}: {{W+|uk|душа́|m}} * {{Z|vec}}: {{W+|vec|ànema|m}} * {{Z|vep}}: {{W-|vep|heng}} * {{Z|vi}}: {{W+|vi|linh hồn}} * {{Z|vro}}: {{W-|vro|heng'}} * {{Z|vot}}: {{W-|vot|entši}} * {{Z|wa}}: {{W+|wa|åme|m}} * {{Z|cy}}: {{W+|cy|enaid|n}},{{W+|cy|ysbryd|n}} * {{Z|km}}: {{W+|km|ព្រលឹង}}, {{W+|km|វិញ្ញាណ}} * {{Z|yai}}: {{W-|yai|ҷон}} * {{Z|yag}}: {{W-|yag|kašpíx}} * {{Z|yi}}: {{W-|yi|נשמה|m|tr=neshome}}, {{W-|yi|נעשאָמע|m}} * {{Z|yoi}}: {{W-|yoi|魂|tr=たまち, tamachi}} * {{Z|el}}: {{W+|el|ψυχή|m}} *: Kevn: {{W-|grc|ψυχή|m}} * {{Z|za}}: {{W-|za|hoenz}} * {{Z|zu}}: {{W+|zu|umoya|c3}} * {{Z|nmn}}: {{W-|nmn|ǃnáã-sé}} {{werger-bin}} :{{werger çavkanî|en|soul|beş=Ev qismê Wergerê|oldid=76368982}} === Rengdêr 1 === {{rengdêr|ku}} # [[giyan]], [[ruh]]a min, [[hêja]], [[qurban]], [[ez-heyran]], [[birêz]], [[rêzdar]] ==== Werger ==== {{werger-ser}} {{werger-bin}} === Rengdêr 2 === {{rengdêr|ku}} # (delal, spehî, rind, xweşik) ''Binere'': '''[[ciwan]]''' == {{ziman|en}} == === Bilêvkirin === * {{deng|en|en-us-can.ogg|dever=DYA}} * {{deng|en|LL-Q1860 (eng)-Back ache-can.wav|Deng|dever=London,QY}} * {{deng|en|LL-Q1860 (eng)-Nattes à chat-can.wav|Deng|dever=}} * {{deng|en|LL-Q1860 (eng)-I learned some phrases-can.wav|Deng|dever=Başûrê Îngilistanê, QY}} * {{deng|en|LL-Q1860 (eng)-She animates-can.wav|Deng|dever=California, DYA}} === Lêker === {{en-tewîn-lk|form=awarte|n3y=can|b=could|pb=could|pn=}} {{lêker|en}} # [[karîn]], [[şiyan]], [[kanîn]], [[tiwanîn]],<br> [[hêz]] bo kirina tiştekê hebûn, <br>[[taqet]] bo kirina tiştekê/î hebûn ==== Hevmane ==== * {{g|en|be able to}} * {{g|en|may}} ==== Dijmane ==== {{kol3|en |cannot |can't |couldn't }}{{bend}} === Navdêr === {{navdêr|en}} # [[kod]], [[kodik]], [[qutî]], [[qotîk]], [[teneke]], <br>[[aman]]ê [[maden]]î yê [[derî]][[girtî]] yê [[xwarin]] tê de têt [[parastin]]. == {{ziman|tr}} == === Bilêvkirin === * {{deng|tr|LL-Q256 (tur)-ToprakM-can.wav|Deng|dever=}} === Navdêr === {{navdêr|tr}} # Can, [[gan]], [[giyan]]. # [[ruh]] #: {{mk|tr|O '''canını''' sana feda eder.|Ew canê xwe fedayî te dike.|rêz=1}} # [[ruh]] #: {{mk|tr|Sende hiç '''can''' yokmuş be''.|Di te de qet can tune lo.|rêz=1}} # Can. #: {{mk|tr|Benle beraber dört '''canız'''.|Tevî min em çar can in.|rêz=1}} # Can. #: {{mk|tr|'''Canım''' gezmek istiyor.|Canê min gerê dixwaze.|rêz=1}} # Can. #: {{mk|tr|Hadi '''canlar''', biraz çabuk olun.|Hade canino, piçek sivik bin.|rêz=1}} # [[ruh]], [[şîrîn]] #: {{mk|tr|Sen ne '''can''' çocuksun.|Tu çi zarokekî mîna can î.|rêz=1}} g44bhg0pm0tzcbqg4hhh3b5ak5gjj7s demdirêj 0 31187 6793997 6724488 2026-10-14T18:27:45Z Wikihez 7344 Kurmancî/Rengdêr: beşa «Hevmane» hate nûkirin (DefSideBoxes) 6793997 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Rengdêr === {{ku-tewîn-rd|demdirêj}} {{rengdêr|ku}} # Tişta/ê ku [[dem]]ek [[dirêj]] [[dom]] kiriye yan dê [[dewam]] bike; tişta/ê ku ji [[mêj]] ve heye yan bûye. #: {{hevmane|ku|domdirêj|dûr û dirêj}} #: {{nm|ku|Ew hevaleke min yê '''demdirêj''' e.}} #* {{jêder|ku|jêgirtin=Akademiya Zanistî ya Padîşahî Swêdî Xelata Sveriges Riksbank di zanistiyên aborî de dide Edmund S. Phelps, ji Zankoya Columbia, NY, DYA û di daxuyaniya xwe dibêje: “Edmund S. Phelps di karên xwe de alîkarî kir, da zanînên li ser girêdana di nav bandorên siyaseta aborî yên '''demdirêj''' û demkurt de kûrtir bibin” û “Ji bo şîtelkirina gorîkirina derfetên navdemî (intertemporal tradeoffs) di siyaseta makroaborî de.|Netkurd.com, 1/2007}} ==== Formên alternatîv ==== {{kol3|ku |demdrêj }} ==== Hevmane ==== {{kol3|ku |çaxdirêj |wextdirêj }} ==== Dijmane ==== {{kol3|ku |domkurt |domkin }} ==== Jê ==== {{kol3|ku |demdirêjî |demdirêjîtî |demdirêjtî }} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|dem<id:wext>|dirêj|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W|de|zeitaufwändig|guman=erê}}, {{W+|de|langfristig}} * {{Z|fa}}: {{W+|fa|بلند مدت}} * {{Z|fi}}: {{W+|fi|pitkäaikainen}} * {{Z|en}}: {{W+|en|long term}} * {{Z|tr}}: {{W+|tr|uzun süreli}}, {{W+|tr|uzun soluklu}} {{werger-bin}} 8opzaejicf5vdocf5qwrv7ynx4ncnq8 tesnîf 0 31299 6794003 6560751 2026-10-14T18:30:42Z Wikihez 7344 /* Navdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794003 wikitext text/x-wiki {{h-bnr|tesnif}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[sinifandin]], [[sefandin]], [[rêz]]kirin #* {{jêder|ku|Salihê Kevirbirî: ''Meng sagh Hrant Dînk - Em hemû Hrant Dînk in'', 1/2007|jêgirtin=hevalê min Huseyîn ligel xebatkara weşanxanê li odeya paş bi karê tasnîfkirina pirtûkan ve mijûl bû.}} ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Formên alternatîv ==== {{kol3|ku |tasnif |tasnîf |tesnif }} ==== Jê ==== {{kol3|ku |tesnîfî |tesnîfker |tesnîf kirin<crz:lêker> |tesnîfkirin<crz:navdêr> |tesnîfkirî<crz:rengdêr> }} ==== Etîmolojî ==== Ji {{deyn|ar|ku}}. {{anlz|ku|te-|sinif}}. ==== Werger ==== {{werger-ser}} * {{Z|tr}}: {{W+|tr|bölümleme}}, {{W+|tr|klasman}}, {{W+|tr|tasnif}} {{werger-bin}} ==== Anagram ==== * {{anagram|ku|a=efînst|fetsîn|fîsten|tenfîs}} l8wr1lkbpoco9whvn0on0mhfsshduev lojîk 0 42153 6793994 6631581 2026-10-14T18:25:53Z Wikihez 7344 Lînkên ser [[jenk]] tê rakirin (via JWB) 6793994 wikitext text/x-wiki {{h-bnr|lojik}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[jîrbêjî]], [[vac]], [[mantiq]], [[beraqilî]], [[hişmendî]], li gor [[hiş]] û [[aqil]]î, ne [[keyfî]], [[feraset]], [[têgihiştin]], [[aqildarî]], [[aqilmendî]], [[aqil]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Formên alternatîv ==== * {{g|ku|locîk}} ==== Jê ==== * {{g|ku|îlojîk}} * {{g|ku|lojîkî}} * {{g|ku|lojîsyen}} ==== Etîmolojî ==== {{etîmon|ku|:deyn|fr:logique|dar=1}} Bi rêya {{deyn|fr|ku|logique}} yan {{hv|en|logic}} ji {{hv|la|logica}} ji {{hv|grc|λογικος}} ji {{g|grc|λόγος|w=[[hiş]], [[aqil]]}}. ==== Werger ==== {{werger-bnr|mantiq}} ==== Anagram ==== * {{anagram|ku|a=îjklo|kolîj}} bf74pg1i7phkn9he80hq81fy625uonx xetat 0 42715 6793977 6464539 2026-10-14T18:21:17Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793977 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === [[Wêne:Arabic Calligraphy at Wazir Khan Mosque.jpg|thumb|250px|berhemek xetatan]] {{navdêr|ku}} # Kesê ku bi [[dest]]an [[nivîs]]ên bi[[nexş]] [[nivîsîn|dinivîse]]. #: {{hevmane|ku|xweşnivîs|nexşnivîs|spehînivîs|kalîgraf|kalîgrafer}} ==== Formên alternatîv ==== * {{g|ku|xettat}} ==== Jê ==== * {{g|ku|xetatî}} * {{g|ku|xetatkarî}} * {{g|ku|xetatvanî}} ==== Etîmolojî ==== Ji {{deyn|ar|ku}}, têkildarî [[xet]] ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W-|de|Schreibkünstler|n}}, {{W-|de|Schreibkünstlerin|m}}, {{W+|de|Kalligraf|n}}, {{W+|de|Kalligraph|n}}, {{W-|de|Kalligrafin|m}}, {{W+|de|Kalligraphin|m}} * {{Z|ast}}: {{W-|ast|calígrafu|n}} * {{Z|az}}: {{W+|az|xəttat}} * {{Z|zh}}: *: {{Z|cmn}}: {{W+|cmn|書法家|tr=shūfǎjiā}}, {{W+|cmn|書家|tr=shūjiā}} * {{Z|ar}}: {{W-|ar|خَطَّاط|n}} * {{Z|fa}}: {{W+|fa|خوشنویس|tr=xošnevis}}, {{W+|fa|خطاط|tr=xattât}} * {{Z|fi}}: {{W-|fi|kalligraafikko}} * {{Z|fr}}: {{W+|fr|calligraphe|n}} * {{Z|gl}}: {{W-|gl|calígrafo|n}} * {{Z|en}}: {{W+|en|calligrapher}} * {{Z|it}}: {{W+|it|calligrafo|n}} * {{Z|ja}}: {{W+|ja|書家|tr=shoka}}, {{W+|ja|書道家|tr=shodōka}} * {{Z|ca}}: {{W-|ca|cal·lígraf|n}} * {{Z|ko}}: {{W+|ko|서예가|sc=Kore}} * {{Z|lv}}: {{W-|lv|kaligrāfs|n}}, {{W-|lv|kaligrāfe|m}} * {{Z|pl}}: {{W+|pl|kaligraf|n}} * {{Z|pt}}: {{W-|pt|calígrafo|n}} * {{Z|ro}}: {{W+|ro|caligraf|n}}, {{W+|ro|caligrafă|m}} * {{Z|ru}}: {{W+|ru|калли́граф|n}} * {{Z|sh}}: *: Kirîlî: {{W-|sh|краснописац|n|sc=Cyrl}}, {{W-|sh|краснописатељ|n|sc=Cyrl}}, {{W-|sh|красописац|n|sc=Cyrl}}, {{W-|sh|красописатељ|n|sc=Cyrl}}, {{W-|sh|краснописатељица|m|sc=Cyrl}}, {{W-|sh|красописатељица|m|sc=Cyrl}} *: Latînî: {{W-|sh|krasnopisac|n|sc=Latn}}, {{W-|sh|krasnopisatelj|n|sc=Latn}}, {{W+|sh|krasopisac|n|sc=Latn}}, {{W-|sh|krasopisatelj|n|sc=Latn}}, {{W-|sh|krasnopisateljica|m|sc=Latn}}, {{W-|sh|krasopisateljica|m|sc=Latn}} * {{Z|es}}: {{W+|es|calígrafo|n}} * {{Z|sv}}: {{W+|sv|kalligraf|g}} * {{Z|tr}}: {{W+|tr|hattat}} * {{Z|vi}}: {{W-|vi|nhà thư pháp}} * {{Z|vo}}: {{W-|vo|kaligrafan}}, {{W-|vo|hikaligrafan}}, {{W-|vo|jikaligrafan}} * {{Z|el}}: {{W+|el|καλλιγράφος|n|sc=Grek}} {{werger-bin}} :{{werger çavkanî|en|calligrapher|beş=Ev qismê Wergerê|oldid=75274175}} goye3v2pqkec9ze3u46a9p35kh13jpl 6794014 6793977 2026-10-14T18:43:36Z Wikihez 7344 Lînkên ser [[spehînivîs]] tê rakirin (via JWB) 6794014 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === [[Wêne:Arabic Calligraphy at Wazir Khan Mosque.jpg|thumb|250px|berhemek xetatan]] {{navdêr|ku}} # Kesê ku bi [[dest]]an [[nivîs]]ên bi[[nexş]] [[nivîsîn|dinivîse]]. #: {{hevmane|ku|xweşnivîs|nexşnivîs|kalîgraf|kalîgrafer}} ==== Formên alternatîv ==== * {{g|ku|xettat}} ==== Jê ==== * {{g|ku|xetatî}} * {{g|ku|xetatkarî}} * {{g|ku|xetatvanî}} ==== Etîmolojî ==== Ji {{deyn|ar|ku}}, têkildarî [[xet]] ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W-|de|Schreibkünstler|n}}, {{W-|de|Schreibkünstlerin|m}}, {{W+|de|Kalligraf|n}}, {{W+|de|Kalligraph|n}}, {{W-|de|Kalligrafin|m}}, {{W+|de|Kalligraphin|m}} * {{Z|ast}}: {{W-|ast|calígrafu|n}} * {{Z|az}}: {{W+|az|xəttat}} * {{Z|zh}}: *: {{Z|cmn}}: {{W+|cmn|書法家|tr=shūfǎjiā}}, {{W+|cmn|書家|tr=shūjiā}} * {{Z|ar}}: {{W-|ar|خَطَّاط|n}} * {{Z|fa}}: {{W+|fa|خوشنویس|tr=xošnevis}}, {{W+|fa|خطاط|tr=xattât}} * {{Z|fi}}: {{W-|fi|kalligraafikko}} * {{Z|fr}}: {{W+|fr|calligraphe|n}} * {{Z|gl}}: {{W-|gl|calígrafo|n}} * {{Z|en}}: {{W+|en|calligrapher}} * {{Z|it}}: {{W+|it|calligrafo|n}} * {{Z|ja}}: {{W+|ja|書家|tr=shoka}}, {{W+|ja|書道家|tr=shodōka}} * {{Z|ca}}: {{W-|ca|cal·lígraf|n}} * {{Z|ko}}: {{W+|ko|서예가|sc=Kore}} * {{Z|lv}}: {{W-|lv|kaligrāfs|n}}, {{W-|lv|kaligrāfe|m}} * {{Z|pl}}: {{W+|pl|kaligraf|n}} * {{Z|pt}}: {{W-|pt|calígrafo|n}} * {{Z|ro}}: {{W+|ro|caligraf|n}}, {{W+|ro|caligrafă|m}} * {{Z|ru}}: {{W+|ru|калли́граф|n}} * {{Z|sh}}: *: Kirîlî: {{W-|sh|краснописац|n|sc=Cyrl}}, {{W-|sh|краснописатељ|n|sc=Cyrl}}, {{W-|sh|красописац|n|sc=Cyrl}}, {{W-|sh|красописатељ|n|sc=Cyrl}}, {{W-|sh|краснописатељица|m|sc=Cyrl}}, {{W-|sh|красописатељица|m|sc=Cyrl}} *: Latînî: {{W-|sh|krasnopisac|n|sc=Latn}}, {{W-|sh|krasnopisatelj|n|sc=Latn}}, {{W+|sh|krasopisac|n|sc=Latn}}, {{W-|sh|krasopisatelj|n|sc=Latn}}, {{W-|sh|krasnopisateljica|m|sc=Latn}}, {{W-|sh|krasopisateljica|m|sc=Latn}} * {{Z|es}}: {{W+|es|calígrafo|n}} * {{Z|sv}}: {{W+|sv|kalligraf|g}} * {{Z|tr}}: {{W+|tr|hattat}} * {{Z|vi}}: {{W-|vi|nhà thư pháp}} * {{Z|vo}}: {{W-|vo|kaligrafan}}, {{W-|vo|hikaligrafan}}, {{W-|vo|jikaligrafan}} * {{Z|el}}: {{W+|el|καλλιγράφος|n|sc=Grek}} {{werger-bin}} :{{werger çavkanî|en|calligrapher|beş=Ev qismê Wergerê|oldid=75274175}} t3nx4mk0shjnqm5q7uo3x0wzrorxbq6 6794030 6794014 2026-10-14T19:19:12Z Wikihez 7344 Kurmancî/Navdêr: beşa «Resim» hate nûkirin, beşa «Etîmolojî» hate nûkirin; Kurmancî: beşa «Navdêr» bû «Navdêr» (DefSideBoxes) 6794030 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === [[Wêne:Arabic Calligraphy at Wazir Khan Mosque.jpg|thumb|250px|Berhemeke '''xetatan'''.]] {{navdêr|ku|c=mnmane}} # Kesê ku bi [[dest]]an [[nivîs]]ên bi[[nexş]] [[nivîsîn|dinivîse]]. #: {{hevmane|ku|xweşnivîs|nexşnivîs|kalîgraf|kalîgrafer}} ==== Formên alternatîv ==== * {{g|ku|xettat}} ==== Jê ==== * {{g|ku|xetatî}} * {{g|ku|xetatkarî}} * {{g|ku|xetatvanî}} ==== Etîmolojî ==== {{etîmon|ku|:deyn|ar:خَطَّاط|metn=++|dar=1}} Têkildarî ''[[xet]]''. ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W-|de|Schreibkünstler|n}}, {{W-|de|Schreibkünstlerin|m}}, {{W+|de|Kalligraf|n}}, {{W+|de|Kalligraph|n}}, {{W-|de|Kalligrafin|m}}, {{W+|de|Kalligraphin|m}} * {{Z|ast}}: {{W-|ast|calígrafu|n}} * {{Z|az}}: {{W+|az|xəttat}} * {{Z|zh}}: *: {{Z|cmn}}: {{W+|cmn|書法家|tr=shūfǎjiā}}, {{W+|cmn|書家|tr=shūjiā}} * {{Z|ar}}: {{W-|ar|خَطَّاط|n}} * {{Z|fa}}: {{W+|fa|خوشنویس|tr=xošnevis}}, {{W+|fa|خطاط|tr=xattât}} * {{Z|fi}}: {{W-|fi|kalligraafikko}} * {{Z|fr}}: {{W+|fr|calligraphe|n}} * {{Z|gl}}: {{W-|gl|calígrafo|n}} * {{Z|en}}: {{W+|en|calligrapher}} * {{Z|it}}: {{W+|it|calligrafo|n}} * {{Z|ja}}: {{W+|ja|書家|tr=shoka}}, {{W+|ja|書道家|tr=shodōka}} * {{Z|ca}}: {{W-|ca|cal·lígraf|n}} * {{Z|ko}}: {{W+|ko|서예가|sc=Kore}} * {{Z|lv}}: {{W-|lv|kaligrāfs|n}}, {{W-|lv|kaligrāfe|m}} * {{Z|pl}}: {{W+|pl|kaligraf|n}} * {{Z|pt}}: {{W-|pt|calígrafo|n}} * {{Z|ro}}: {{W+|ro|caligraf|n}}, {{W+|ro|caligrafă|m}} * {{Z|ru}}: {{W+|ru|калли́граф|n}} * {{Z|sh}}: *: Kirîlî: {{W-|sh|краснописац|n|sc=Cyrl}}, {{W-|sh|краснописатељ|n|sc=Cyrl}}, {{W-|sh|красописац|n|sc=Cyrl}}, {{W-|sh|красописатељ|n|sc=Cyrl}}, {{W-|sh|краснописатељица|m|sc=Cyrl}}, {{W-|sh|красописатељица|m|sc=Cyrl}} *: Latînî: {{W-|sh|krasnopisac|n|sc=Latn}}, {{W-|sh|krasnopisatelj|n|sc=Latn}}, {{W+|sh|krasopisac|n|sc=Latn}}, {{W-|sh|krasopisatelj|n|sc=Latn}}, {{W-|sh|krasnopisateljica|m|sc=Latn}}, {{W-|sh|krasopisateljica|m|sc=Latn}} * {{Z|es}}: {{W+|es|calígrafo|n}} * {{Z|sv}}: {{W+|sv|kalligraf|g}} * {{Z|tr}}: {{W+|tr|hattat}} * {{Z|vi}}: {{W-|vi|nhà thư pháp}} * {{Z|vo}}: {{W-|vo|kaligrafan}}, {{W-|vo|hikaligrafan}}, {{W-|vo|jikaligrafan}} * {{Z|el}}: {{W+|el|καλλιγράφος|n|sc=Grek}} {{werger-bin}} :{{werger çavkanî|en|calligrapher|beş=Ev qismê Wergerê|oldid=75274175}} di64hj5pibrss4z5ukwnrzl9zs7fs3h melaq 0 51329 6793947 6793933 2026-10-14T17:11:13Z Wikihez 7344 Kurmancî/Rengdêr: mane, hevmane hatin guhartin, beşa «Etîmolojî» hate nûkirin (DefSideBoxes) 6793947 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Rengdêr === {{ku-tewîn-rd|melaq}} {{rengdêr|ku}} # Kesa/ê ku tiştekî dibêje lê dijberî gotina xwe dike, kesa/ê ku hem [[dost]] e û hem jî [[dijmin]] ê. #: {{hevmane|ku|durû|riyakar|bêexlaq|dudev|bincam|çîvok|derewîn}} ==== Jê ==== * [[melaqane]] * [[melaqî]] * [[melaqîtî]] * [[melaqtî]] ==== Etîmolojî ==== {{etîmon|ku|:deyn|ar:ملاق<ts:mellaq>|metn=++|dar=1}} ==== Bide ber ==== * [[meraq]] ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W|de|Heuchler|m}} * {{Z|cs}}: {{W|cs|pokrytec|m}} * {{Z|zh}}: {{W+|zh|伪善者}}, {{W+|zh|伪君子}}, [[乡愿]] [[偽善者]], {{W+|zh|偽君子}}, {{W+|zh|鄉愿}} * {{Z|ee}}: {{W+|ee|alakpatɔ}} * {{Z|fa}}: {{W+|fa|شارلاتان}}, {{W+|fa|کلاش}} * {{Z|fi}}: {{W+|fi|tekopyhä}}, {{W+|fi|kaksinaamainen}} * {{Z|fr}}: {{W|fr|hypocrite|m}} * {{Z|en}}: {{W+|en|hypocrite}}, {{W|en|jester|guman=erê}} * {{Z|ca}}: {{W+|ca|hipòcrita}} * {{Z|ko}}: {{W|ko|위선자|sc=Hang}} * {{Z|no}}: {{W|no|hykler|m}} * {{Z|pt}}: {{W+|pt|hipócrita}} * {{Z|ro}}: {{W|ro|ipocrit|m}}, {{W|ro|ipocrită|f}} * {{Z|sh}}: {{W|sh|ханжа|sc=Cyrl}}, {{W|sh|licemer|m}}, {{W|sh|licemerka|f}}, {{W|sh|dvoličnik|m}}, {{W|sh|dvoličnjak|m}}, {{W|sh|dvoličnjakinja|f}}, {{W|sh|himbenik|m}} * {{Z|es}}: {{W+|es|hipócrita}} * {{Z|sv}}: {{W|sv|hycklare|c}} * {{Z|tr}}: {{W+|tr|ikiyüzlü}}, {{W+|tr|riyakâr}}, {{W+|tr|huluskâr}}, {{W+|tr|mütebasbıs}}, {{W+|tr|yalaka}}, {{W+|tr|kavukçu}}, {{W+|tr|yalpak}}, {{W+|tr|yaltak}}, {{W+|tr|cilveli}}, {{W+|tr|çığırtkan}}, {{W|tr|cıvık|guman=erê}}, {{W|tr|cıvık ağızlı|guman=erê}}, {{W|tr|hulûskâr|guman=erê}} * {{Z|el}}: {{W|el|υποκριτής|m}}, {{W|el|υποκρίτρια|f}} {{werger-bin}} ==== Anagram ==== * {{anagram|ku|a=aelmq|eqlam|qalem}} rqd7yallcbr0fqrawcxixu6w9wkuu3g 6793948 6793947 2026-10-14T17:11:47Z Wikihez 7344 ([[m:Special:MyLanguage/User:Jon Harald Søby/diffedit|diffedit]]) 6793948 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Rengdêr === {{ku-tewîn-rd|melaq}} {{rengdêr|ku}} # Kesa/ê ku tiştekî dibêje lê dijberî gotina xwe dike, kesa/ê ku hem [[dost]] e û hem jî [[dijmin]] e. #: {{hevmane|ku|durû|riyakar|bêexlaq|dudev|bincam|çîvok|derewîn}} ==== Jê ==== * [[melaqane]] * [[melaqî]] * [[melaqîtî]] * [[melaqtî]] ==== Etîmolojî ==== {{etîmon|ku|:deyn|ar:ملاق<ts:mellaq>|metn=++|dar=1}} ==== Bide ber ==== * [[meraq]] ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W|de|Heuchler|m}} * {{Z|cs}}: {{W|cs|pokrytec|m}} * {{Z|zh}}: {{W+|zh|伪善者}}, {{W+|zh|伪君子}}, [[乡愿]] [[偽善者]], {{W+|zh|偽君子}}, {{W+|zh|鄉愿}} * {{Z|ee}}: {{W+|ee|alakpatɔ}} * {{Z|fa}}: {{W+|fa|شارلاتان}}, {{W+|fa|کلاش}} * {{Z|fi}}: {{W+|fi|tekopyhä}}, {{W+|fi|kaksinaamainen}} * {{Z|fr}}: {{W|fr|hypocrite|m}} * {{Z|en}}: {{W+|en|hypocrite}}, {{W|en|jester|guman=erê}} * {{Z|ca}}: {{W+|ca|hipòcrita}} * {{Z|ko}}: {{W|ko|위선자|sc=Hang}} * {{Z|no}}: {{W|no|hykler|m}} * {{Z|pt}}: {{W+|pt|hipócrita}} * {{Z|ro}}: {{W|ro|ipocrit|m}}, {{W|ro|ipocrită|f}} * {{Z|sh}}: {{W|sh|ханжа|sc=Cyrl}}, {{W|sh|licemer|m}}, {{W|sh|licemerka|f}}, {{W|sh|dvoličnik|m}}, {{W|sh|dvoličnjak|m}}, {{W|sh|dvoličnjakinja|f}}, {{W|sh|himbenik|m}} * {{Z|es}}: {{W+|es|hipócrita}} * {{Z|sv}}: {{W|sv|hycklare|c}} * {{Z|tr}}: {{W+|tr|ikiyüzlü}}, {{W+|tr|riyakâr}}, {{W+|tr|huluskâr}}, {{W+|tr|mütebasbıs}}, {{W+|tr|yalaka}}, {{W+|tr|kavukçu}}, {{W+|tr|yalpak}}, {{W+|tr|yaltak}}, {{W+|tr|cilveli}}, {{W+|tr|çığırtkan}}, {{W|tr|cıvık|guman=erê}}, {{W|tr|cıvık ağızlı|guman=erê}}, {{W|tr|hulûskâr|guman=erê}} * {{Z|el}}: {{W|el|υποκριτής|m}}, {{W|el|υποκρίτρια|f}} {{werger-bin}} ==== Anagram ==== * {{anagram|ku|a=aelmq|eqlam|qalem}} rtnocjnxlzghzc6vz917hpe5kbizzol wad 0 51687 6793957 6731819 2026-10-14T17:59:03Z Wikihez 7344 Kurmancî/Navdêr: beşa «Etîmolojî» hate nûkirin (DefSideBoxes) 6793957 wikitext text/x-wiki {{h-bnr|-wad|wa'd}} == {{ziman|ku}} == === Navdêr === {{navdêr|ku|z=m}} # [[soz]], [[ehd]], [[belên]], [[newîn]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Formên alternatîv ==== {{kol3|ku |waad |wahd |wa'd |wehd |we'd |weid |waid }} ==== Etîmolojî ==== {{etîmon|ku|:deyn|ar:وَعْد|metn=++|id=ehd|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Versprechen|n}} * {{Z|cs}}: {{W+|cs|slib|m}} * {{Z|zh}}: [[诺言]] (nuòyán) * {{Z|da}}: {{W+|da|løfte|n}} * {{Z|ar}}: {{W-|ar|وعد}} (waʕd) * {{Z|ee}}: {{W-|ee|ŋugbedodo}} * {{Z|fa}}: {{W+|fa|پیمان}}, {{W+|fa|وعده}}, {{W-|fa|قول و قرار}} * {{Z|fi}}: {{W+|fi|lupaus}} * {{Z|fr}}: {{W+|fr|vœu|m}}, {{W+|fr|promesse|f}} * {{Z|nl}}: {{W+|nl|belofte|f}} * {{Z|he}}: {{W+|he|הבטחה|tr=havtaḥa}}, {{W+|he|נדר|tr=neder}} * {{Z|ang}}: {{W-|ang|hat|cuda=hāt}} * {{Z|en}}: {{W+|en|promise}}, {{W+|en|vow}}, {{W+|en|affirmation}}, {{W+|en|oath}} * {{Z|it}}: {{W+|it|promessa|f}} * {{Z|is}}: {{W-|is|loforð|n}} * {{Z|ja}}: {{W+|ja|約束|tr=やくそく, yakusoku}} * {{Z|ca}}: {{W+|ca|promesa|f}} * {{Z|ko}}: [[약속]] (yaksok) * {{Z|hu}}: {{W+|hu|ígéret}} * {{Z|no}}: {{W+|no|løfte|n}} * {{Z|pl}}: {{W+|pl|obietnica|f}} * {{Z|pt}}: {{W+|pt|promessa|f}} * {{Z|ro}}: {{W+|ro|promisiune|f}} * {{Z|ru}}: {{W+|ru|обещание|tr=obeščánije|n}} * {{Z|sl}}: {{W+|sl|obljuba|f}} * {{Z|es}}: {{W+|es|promesa|f}} * {{Z|sv}}: {{W+|sv|löfte|c}} * {{Z|te}}: [[ఒట్టు]] (ottu) * {{Z|tr}}: {{W+|tr|söz}}, {{W+|tr|vaat}} {{werger-bin}} ==== Anagram ==== * {{anagram|ku|a=adw|daw}} == {{ziman|en}} == === Bilêvkirin === * {{deng|en|en-us-wad.ogg|dever=DYA}} === Navdêr === {{navdêr|en}} # [[deste]], [[gurze]], gelek dirav<ref>Ferhenga Kurdî-Îngîlîzî ya Salah Sadallah</ref> ==== Anagram ==== * {{anagram|en|a=adw|Daw|daw}} ==== Çavkanî ==== {{çavkanî}} bfzurfmui2wflydow8lfrf39uvpw01c vac 0 62171 6793996 6532054 2026-10-14T18:25:59Z Wikihez 7344 Lînkên ser [[jenk]] tê rakirin (via JWB) 6793996 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku}} # [[mantiq]], [[lojîk]], [[beraqilî]], [[jîrbêjî]] li gor hiş û aqilî, ne keyfî ==== Formên alternatîv ==== ji derveyî vacê, bêvac, bivac ==== Etîmolojî ==== {{bêetîmolojî|ku}} ==== Bi zaravayên din ==== * Soranî: [[jîrbêjî]] ==== Werger ==== {{werger-ser}} * {{Z|tr}}: {{W+|tr|mantık}} {{werger-bin}} mc0khes2qhq01suf8l2447ukdd1zae4 mantiq 0 63786 6793995 6732290 2026-10-14T18:25:56Z Wikihez 7344 Lînkên ser [[jenk]] tê rakirin (via JWB) 6793995 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[lojîk]], [[hişmendî]], [[aqilmendî]], [[aqil]], [[zihndarî]], [[zihniyet]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Bi alfabeyên din ==== * {{ku-ar|مانتق}} ==== Formên alternatîv ==== * [[mentiq]] * [[mantix]] ==== Jê ==== {{kol3|ku |bêmantiq |bêmantiqî |bimantiq |mantiqdar |mantiqdarî |mantiqî }} ==== Etîmolojî ==== {{etîmon|ku|:drşnd|ar:منطق<tr:menṭiq>|dar=1}} Ji {{ji|ar|ku|منطق|tr=menṭiq}} ji {{g|ar|نطق|tr=nuṭq|w=[[peyv]], [[axivîn]], [[gotin]]}} ji lêker {{g|ku|نطق|tr=neṭeqe|w=[[axivîn]]}}, wergerra yekser ya peyva yûnanî [[λογική]] (logikê: mentiq, [[lojîk]]) ku ji peyva [[λόγος]] (logos: [[peyv]]; [[hiş]]) hatiye çêkirin. ==== Werger ==== {{werger-ser}} * {{Z|sq}}: {{W+|sq|logjikë|m}} * {{Z|de}}: {{W+|de|Logik|m}} * {{Z|az}}: {{W+|az|məntiq}} * {{Z|eu}}: {{W+|eu|logika}} * {{Z|bn}}: {{W+|bn|মন্তেক}} * {{Z|bg}}: {{W+|bg|ло́гика|m}} * {{Z|cs}}: {{W+|cs|logika|m}} * {{Z|zh}}: *: {{Z|cmn}}: {{W+|cmn|邏輯|tr=luóji}}, {{W+|cmn|論理|tr=lùnlǐ}} * {{Z|id}}: {{W+|id|logika}}, {{W+|id|mantik}} * {{Z|ar}}: {{W-|ar|مَنْطِق|n}} * {{Z|hy}}: {{W+|hy|տրամաբանություն}} * {{Z|eo}}: {{W-|eo|logiko}} * {{Z|fa}}: {{W+|fa|منطق|tr=manteq}} * {{Z|fi}}: {{W+|fi|logiikka}} * {{Z|fr}}: {{W+|fr|logique|m}} * {{Z|gl}}: {{W+|gl|lóxica|m}} * {{Z|hi}}: {{W+|hi|तर्क}} * {{Z|nl}}: {{W+|nl|logica|m}} * {{Z|he}}: {{W+|he|הגיון|tr=higayon}} * {{Z|ang}}: {{W-|ang|flitcræft|n}} * {{Z|en}}: {{W+|en|logic}} * {{Z|ga}}: {{W-|ga|loighic|m}} * {{Z|it}}: {{W+|it|logica|m}}, {{W+|it|logica matematica|m}} * {{Z|is}}: {{W-|is|rökfræði|m}} * {{Z|ja}}: {{W+|ja|論理|tr=ろんり, ronri}} * {{Z|ca}}: {{W+|ca|lògica|m}} * {{Z|ko}}: {{W+|ko|논리}} * {{Z|la}}: {{W-|la|dialectica|m}} * {{Z|lv}}: {{W-|lv|loģika|m}} * {{Z|mk}}: {{W-|mk|ло́гика|m}} * {{Z|hu}}: {{W+|hu|logika}} * {{Z|no}}: {{W-|no|logikk|n}} * {{Z|ug}}: {{W-|ug|مەنتىق}} * {{Z|pl}}: {{W+|pl|logika|m}} * {{Z|pt}}: {{W+|pt|lógica|m}} * {{Z|ro}}: {{W+|ro|logică|m}} * {{Z|ru}}: {{W+|ru|ло́гика|m}} * {{Z|sa}}: {{W-|sa|न्यायम्}} * {{Z|sh}}: {{W+|sh|lògika|m}} * {{Z|sk}}: {{W-|sk|logika|m}} * {{Z|sl}}: {{W+|sl|logika|m}} * {{Z|es}}: {{W+|es|lógica|m}} * {{Z|sw}}: {{W+|sw|mantiki}} * {{Z|sv}}: {{W+|sv|logik|g}} * {{Z|tl}}: {{W-|tl|matwiran}} * {{Z|th}}: {{W+|th|ตรรกะ}} * {{Z|te}}: {{W+|te|తర్కము}} * {{Z|tr}}: {{W+|tr|mantık}} * {{Z|tpi}}: {{W-|tpi|lajik}} * {{Z|ur}}: {{W-|ur|ترک|tr=tark}} * {{Z|uk}}: {{W+|uk|ло́гіка|m}} * {{Z|vi}}: {{W+|vi|logic}}, {{W-|vi|lô-gíc}}, {{W+|vi|luận lý}} * {{Z|cy}}: {{W+|cy|rhesymeg|m}} * {{Z|km}}: {{W+|km|តក្ក|sc=Khmr|tr=takka}} * {{Z|xh}}: {{W-|xh|i-logic}} * {{Z|yi}}: {{W-|yi|לאָגיק|m}} * {{Z|el}}: {{W+|el|λογική|m}} {{werger-bin}} :{{werger çavkanî|en|logic|beş=Ev qismê Wergerê|oldid=75874211}} tsg7if68jimlg4c1iw9ss3xj9az0soi kemoterapî 0 115232 6794007 6645226 2026-10-14T18:39:40Z Wikihez 7344 Kurmancî/Navdêr: mane hate guhartin, beşa «Hevmane» hate rakirin (DefSideBoxes) 6794007 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[derman|Derman]]kirina bi [[made]]yên [[kîmyawî]] bo [[kuştin]]a yan [[rawestandin]]a [[şêrpence]]yê. #* {{jêder|ku|jêgirtin=Radyoterapî qediya, gelî dostan. Piştî navbireke neh-deh rojan dê dest bi '''kemoterapiyê''' bikin.|Arjen Arî di Facebookê de, 10/2012}} ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Formên alternatîv ==== * {{g|ku|şîmyoterapî}} ==== Jê ==== * {{g|ku|kemoterapîk}} * {{g|ku|kemoterapîst}} * {{g|ku|kemoterapîstî}} ==== Etîmolojî ==== {{etîmon|ku|:drşnd|en:chemotherapy|dar=1}} Ji {{ji|en|ku|chemotherapy}} û ''[[şîmyoterapî]]'' ji {{ji|fr|ku|chimiothérapie}}, têkilî [[kîmya]] û [[terapî]] ==== Bide ber ==== * {{g|ku|radyoterapî}} ==== Werger ==== {{werger-ser}} * {{Z|fi}}: {{W+|fi|kemoterapia}} * {{Z|fr}}: {{W+|fr|chimiothérapie}} * {{Z|en}}: {{W+|en|chemotherapy}} {{werger-bin}} l4r5im3rs4zubgrprhrnwffie1wh4zp 6794063 6794007 2026-10-14T20:12:23Z Balyozbot 7867 Bot: Paqijiyên kozmetîk (Lînk paqij kir.) 6794063 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[derman|Dermankirina]] bi [[made]]yên [[kîmyawî]] bo [[kuştin]]a yan [[rawestandin]]a [[şêrpence]]yê. #* {{jêder|ku|jêgirtin=Radyoterapî qediya, gelî dostan. Piştî navbireke neh-deh rojan dê dest bi '''kemoterapiyê''' bikin.|Arjen Arî di Facebookê de, 10/2012}} ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Formên alternatîv ==== * {{g|ku|şîmyoterapî}} ==== Jê ==== * {{g|ku|kemoterapîk}} * {{g|ku|kemoterapîst}} * {{g|ku|kemoterapîstî}} ==== Etîmolojî ==== {{etîmon|ku|:drşnd|en:chemotherapy|dar=1}} Ji {{ji|en|ku|chemotherapy}} û ''[[şîmyoterapî]]'' ji {{ji|fr|ku|chimiothérapie}}, têkilî [[kîmya]] û [[terapî]] ==== Bide ber ==== * {{g|ku|radyoterapî}} ==== Werger ==== {{werger-ser}} * {{Z|fi}}: {{W+|fi|kemoterapia}} * {{Z|fr}}: {{W+|fr|chimiothérapie}} * {{Z|en}}: {{W+|en|chemotherapy}} {{werger-bin}} jwq6o0r8g1ypc79ee2j4gr4izgkcqt0 hattat 0 140780 6793972 4612888 2026-10-14T18:20:55Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793972 wikitext text/x-wiki == {{ziman|tr}} == === Navdêr === {{navdêr|tr}} # [[xweşnivîs]], [[paknivîs]], (ê ku destxetên wî xweşik in) # [[hêlkar]]; xetkar, [[xetkêş]] (kesê ku paknivîsiyê dike) 57jcskqr7tziz2nkz4jtzzoz04bvlka mezuniyet 0 154248 6794075 6619840 2026-10-14T20:31:27Z Wikihez 7344 /* Navdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794075 wikitext text/x-wiki {{h-bnr|mezûniyet}} == {{ziman|tr}} == === Navdêr === {{navdêr|tr}} # [[mezûnî]], [[mezûniyet]] 7hmc6lta9iptv4ryrrwgvbu2qifww6p dengaz 0 178807 6793961 6279924 2026-10-14T18:16:38Z Wikihez 7344 Kurmancî/Mane: beşa «Çavkanî» hate zêdekirin; Kurmancî: beşa «Mane» bû «Rengdêr» (DefSideBoxes) 6793961 wikitext text/x-wiki == {{ziman|ku}} == === Rengdêr === {{rengdêr|ku}} # [[bêdeng]], [[kêmaxêv]] ==== Etîmolojî ==== {{bêetîmolojî|ku}} ==== Werger ==== {{werger-ser}} * {{Z|fa}}: {{W+|fa|کم حرف}}, {{W+|fa|ساکت}} {{werger-bin}} ==== Çavkanî ==== * {{kitêb|ku|nivîskar=Sinan Alptekin|sernav=Ferhenga Zimanê Kurdî - Tirkî|sal=2003|rûpel=26}} * {{Ç:Demîrhan:Ferhenga Destî|rûpel=87}} 7lx9b4ko0vu7tl5xg1ibwbd87ratbur 6793963 6793961 2026-10-14T18:18:02Z Wikihez 7344 Requesting speedy deletion (Nehat peyitandin). ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]]) 6793963 wikitext text/x-wiki {{jêbirin|1=Nehat peyitandin}} == {{ziman|ku}} == === Rengdêr === {{rengdêr|ku}} # [[bêdeng]], [[kêmaxêv]] ==== Etîmolojî ==== {{bêetîmolojî|ku}} ==== Werger ==== {{werger-ser}} * {{Z|fa}}: {{W+|fa|کم حرف}}, {{W+|fa|ساکت}} {{werger-bin}} ==== Çavkanî ==== * {{kitêb|ku|nivîskar=Sinan Alptekin|sernav=Ferhenga Zimanê Kurdî - Tirkî|sal=2003|rûpel=26}} * {{Ç:Demîrhan:Ferhenga Destî|rûpel=87}} 56mhymf9xxpcsl4kmpru15jxqn58waj 6794016 6793963 2026-10-14T18:46:01Z Wikihez 7344 /* Werger */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794016 wikitext text/x-wiki {{jêbirin|1=Nehat peyitandin}} == {{ziman|ku}} == === Rengdêr === {{rengdêr|ku}} # [[bêdeng]], [[kêmaxêv]] ==== Etîmolojî ==== {{bêetîmolojî|ku}} ==== Werger ==== {{werger-ser/ceribandin}} * {{Z|fa}}: {{W+|fa|کم حرف}}, {{W+|fa|ساکت}} {{werger-bin/ceribandin}} ==== Çavkanî ==== * {{kitêb|ku|nivîskar=Sinan Alptekin|sernav=Ferhenga Zimanê Kurdî - Tirkî|sal=2003|rûpel=26}} * {{Ç:Demîrhan:Ferhenga Destî|rûpel=87}} egrsqsgmtyqbaxwccr23ujjw3hw7okg 6794018 6794016 2026-10-14T18:56:08Z Wikihez 7344 Restored revision 6793963 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]]) 6794018 wikitext text/x-wiki {{jêbirin|1=Nehat peyitandin}} == {{ziman|ku}} == === Rengdêr === {{rengdêr|ku}} # [[bêdeng]], [[kêmaxêv]] ==== Etîmolojî ==== {{bêetîmolojî|ku}} ==== Werger ==== {{werger-ser}} * {{Z|fa}}: {{W+|fa|کم حرف}}, {{W+|fa|ساکت}} {{werger-bin}} ==== Çavkanî ==== * {{kitêb|ku|nivîskar=Sinan Alptekin|sernav=Ferhenga Zimanê Kurdî - Tirkî|sal=2003|rûpel=26}} * {{Ç:Demîrhan:Ferhenga Destî|rûpel=87}} 56mhymf9xxpcsl4kmpru15jxqn58waj چین کردن 0 272373 6793999 4914449 2026-10-14T18:29:52Z Wikihez 7344 /* Lêker */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6793999 wikitext text/x-wiki == {{ziman|ckb}} == === Bilêvkirin === :çîn kirdin === Lêker === {{lêker|ckb}} # [[sinifandin]], [[sefandin]] glidxmtw15k243tltv1vpifc7snsxge 6794001 6793999 2026-10-14T18:30:16Z Wikihez 7344 /* Soranî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794001 wikitext text/x-wiki == {{ziman|ckb}} == === Lêker === {{lêker|ckb|tr=çîn kirdin}} # [[sinifandin]], [[sefandin]] nv55ejle5f5nj917v79d75bw89jsfo8 disappointed 0 274580 6793952 6793928 2026-10-14T17:16:26Z Wikihez 7344 ([[m:Special:MyLanguage/User:Jon Harald Søby/diffedit|diffedit]]) 6793952 wikitext text/x-wiki == {{ziman|en}} == === Bilêvkirin === * {{IPA|en|/ˌdɪsəˈpɔɪntɪd/}} * {{deng|en|en-us-disappointed.ogg|dever=DYA}} * {{deng|en|LL-Q1860 (eng)-Grendelkhan-disappointed.wav|Deng|dever=Connecticut, DYA}} === Rengdêr === {{en-rengdêr}} # [[mirad]] [[di ber de man|di ber de mayî]],[[xeyalşikestî]], [[hêvîşikestî]] ==== Hevmane ==== * {{g|en|discomfited}} * {{g|en|foiled}} * {{g|en|frustrated}} * {{g|en|thwarted}} ==== Jê ==== * {{g|en|disappointedly}} * {{g|en|disappointedness}} fowngn544jksnoin8gb0kbu08xiv0kc 6793953 6793952 2026-10-14T17:16:51Z Wikihez 7344 ([[m:Special:MyLanguage/User:Jon Harald Søby/diffedit|diffedit]]) 6793953 wikitext text/x-wiki == {{ziman|en}} == === Bilêvkirin === * {{IPA|en|/ˌdɪsəˈpɔɪntɪd/}} * {{deng|en|en-us-disappointed.ogg|dever=DYA}} * {{deng|en|LL-Q1860 (eng)-Grendelkhan-disappointed.wav|Deng|dever=Connecticut, DYA}} === Rengdêr === {{en-rengdêr}} # [[mirad]] [[di ber de man|di ber de mayî]], [[xeyalşikestî]], [[hêvîşikestî]] ==== Hevmane ==== * {{g|en|discomfited}} * {{g|en|foiled}} * {{g|en|frustrated}} * {{g|en|thwarted}} ==== Jê ==== * {{g|en|disappointedly}} * {{g|en|disappointedness}} 263aei8tesgco17ur8n426cxr1e80hg alînegirî 0 292387 6793988 6725993 2026-10-14T18:24:49Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793988 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # bê tereftî, ne alî girtî # [[nêtarî]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Hevmane ==== {{kol3|ku |bêalîtî |bêhêlî |bêalîbûn |bêhêlibûn }} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|alînegir|-î|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Neutralität|m}}, {{W-|de|Unparteilichkeit|m}} * {{Z|az}}: {{W-|az|neytrallıq}} * {{Z|zh}}: {{W+|zh|中立}} * {{Z|ar}}: {{W-|ar|حياد}} * {{Z|fa}}: {{W+|fa|بیطرفی}}, {{W-|fa|بی طرفی}} * {{Z|fr}}: {{W+|fr|neutralité}} * {{Z|en}}: {{W+|en|neutrality}}, {{W+|en|disinterest}}, {{W+|en|dispassionateness}}, {{W+|en|objectivity}} * {{Z|no}}: {{W+|no|nøytralitet}} * {{Z|ru}}: {{W+|ru|нейтралитет}} * {{Z|es}}: {{W+|es|neutralidad}} * {{Z|sv}}: {{W+|sv|neutralitet}} * {{Z|tr}}: {{W+|tr|tarafsızlık}}, {{W+|tr|bitaraflık}}, {{W+|tr|yansızlık}} {{werger-bin}} 2cu9h26yy1bqkc29tp6xl3mc9yl29n4 bêalîtî 0 292388 6793990 6726812 2026-10-14T18:25:00Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793990 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # bê tereftî, ne alî girtî ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Hevmane ==== {{kol3|ku |alînegirî |bêhêlî |bêalîbûn |bêhêlibûn }} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|bêalî|-tî|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Neutralität|f}}, {{W-|de|Unparteilichkeit|f}} * {{Z|az}}: {{W-|az|neytrallıq}} * {{Z|zh}}: {{W+|zh|中立}} * {{Z|ar}}: {{W-|ar|حياد}} * {{Z|fa}}: {{W+|fa|بیطرفی}} * {{Z|fr}}: {{W+|fr|neutralité}} * {{Z|en}}: {{W+|en|neutrality}}, {{W+|en|dispassionateness}} * {{Z|no}}: {{W+|no|nøytralitet}} * {{Z|ru}}: {{W+|ru|нейтралитет}} * {{Z|es}}: {{W+|es|neutralidad}} * {{Z|sv}}: {{W+|sv|neutralitet}} * {{Z|tr}}: {{W+|tr|tarafsızlık}}, {{W+|tr|yansızlık}} {{werger-bin}} p5q30m89ij3en2vjzio7l8lose7zqt2 bêhêlî 0 292389 6793991 6726893 2026-10-14T18:25:04Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793991 wikitext text/x-wiki {{h-bnr|bêhelî}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # bê tereftî, ne alî girtî ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Hevmane ==== {{kol3|ku |bêalîtî |alînegirî |bêalîbûn |bêhêlibûn }} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|bêhêl|-î|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Neutralität|f}}, {{W-|de|Unparteilichkeit|f}}, {{W+|de|Sachlichkeit|f|guman=erê}} * {{Z|az}}: {{W-|az|neytrallıq}} * {{Z|zh}}: {{W+|zh|中立}} * {{Z|ar}}: {{W-|ar|حياد}} * {{Z|fa}}: {{W+|fa|بیطرفی}} * {{Z|fr}}: {{W+|fr|neutralité}} * {{Z|en}}: {{W+|en|neutrality}} * {{Z|no}}: {{W+|no|nøytralitet}} * {{Z|ru}}: {{W+|ru|нейтралитет}} * {{Z|es}}: {{W+|es|neutralidad}} * {{Z|sv}}: {{W+|sv|neutralitet}} * {{Z|tr}}: {{W+|tr|tarafsızlık}}, {{W+|tr|aynasızlık}}, {{W+|tr|aynasız}} {{werger-bin}} 9hpjjsy6m3ehnyzw4nluwt2fz7b0px8 bêalîbûn 0 292391 6793989 6726811 2026-10-14T18:24:56Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793989 wikitext text/x-wiki {{h-bnr|bêalî bûn}} {{bnr-l|bêalî bûn}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # bê tereftî, ne alî girtî ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Hevmane ==== {{kol3|ku |bêalîtî |bêhêlî |alînegirî |bêhêlibûn }} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|bêalî|bûn|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Neutralität|f}}, {{W-|de|Unparteilichkeit|f}} * {{Z|az}}: {{W-|az|neytrallıq}} * {{Z|zh}}: {{W+|zh|中立}} * {{Z|ar}}: {{W-|ar|حياد}} * {{Z|fa}}: {{W+|fa|بیطرفی}} * {{Z|fr}}: {{W+|fr|neutralité}} * {{Z|en}}: {{W+|en|neutrality}} * {{Z|no}}: {{W+|no|nøytralitet}} * {{Z|ru}}: {{W+|ru|нейтралитет}} * {{Z|es}}: {{W+|es|neutralidad}} * {{Z|sv}}: {{W+|sv|neutralitet}} * {{Z|tr}}: {{W+|tr|tarafsızlık}}, {{W+|tr|bitaraflık}} {{werger-bin}} rw5u55dr8xpkcr7pvqlynxjrtkd7fid frustrated 0 309047 6793954 6793930 2026-10-14T17:17:26Z Wikihez 7344 Îngilîzî: beşa «Mane» bû «Rengdêr» (DefSideBoxes) 6793954 wikitext text/x-wiki == {{ziman|en}} == === Rengdêr === {{rengdêr|en}} # [[acizbûyî]], [[aciz]] # [[xeyalşikestî]], [[hêvîşikestî]] kjw1x4yuyn5tijhmn171pix63ppwv06 Neutralität 0 309174 6793986 4650521 2026-10-14T18:24:38Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793986 wikitext text/x-wiki == {{ziman|de}} == === Navdêr === {{navdêr|de|z=m}} # [[alînegirî]], [[bêalîbûn]], [[bêalîtî]], [[bêhêlî]] rtg0677rqh2sjar9bhazenbdhr37j4l Unparteilichkeit 0 309175 6793987 4651624 2026-10-14T18:24:41Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793987 wikitext text/x-wiki == {{ziman|de}} == === Navdêr === {{navdêr|de|z=m}} # [[alînegirî]], [[bêalîbûn]], [[bêalîtî]], [[bêhêlî]] rtg0677rqh2sjar9bhazenbdhr37j4l بیطرفی 0 310939 6793992 5863600 2026-10-14T18:25:08Z Wikihez 7344 Lînkên ser [[inyennegirî]] tê rakirin (via JWB) 6793992 wikitext text/x-wiki == {{ziman|fa}} == === Mane === {{bêcure|fa}} # [[alînegirî]], [[bêalîbûn]], [[bêalîtî]], [[bêhêlî]] 8u49pjm37lys9c5o4l4bcexql8xuaew nexşnivîs 0 344212 6793975 6313351 2026-10-14T18:21:11Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793975 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[spehînivîs]], [[kalîgraf]], [[kalîgrafer]] ==== Tewandin ==== {{ku-tewîn-nav/nû|mê}} 7qpko7ft8u95rxemuqbga9faiith4nn 6794013 6793975 2026-10-14T18:43:33Z Wikihez 7344 Lînkên ser [[spehînivîs]] tê rakirin (via JWB) 6794013 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[kalîgraf]], [[kalîgrafer]] ==== Tewandin ==== {{ku-tewîn-nav/nû|mê}} 2sqljkbduop6oyvirjockctdi9yh4md 6794060 6794013 2026-10-14T20:12:14Z Balyozbot 7867 Bot: Paqijiyên kozmetîk ({{ku-tewîn-nav/nû}} → {{ku-tewîn-nav}}.) 6794060 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[kalîgraf]], [[kalîgrafer]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} 7gcesp2iabvcia1bdimpiaa94cb2lhb kalîgraf 0 344215 6793973 6693264 2026-10-14T18:21:02Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793973 wikitext text/x-wiki {{h-bnr|kaligraf}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[nexşnivîs]], [[spehînivîser]] # {{kontrol|rêz=erê|[[spehînivîs]], [[kalîgrafer]]}} ==== Tewandin ==== {{ku-tewîn-nav|mê}} tojfr2eo6zfhy9pi4n845zdubeu6i1w 6794011 6793973 2026-10-14T18:43:28Z Wikihez 7344 Lînkên ser [[spehînivîs]] tê rakirin (via JWB) 6794011 wikitext text/x-wiki {{h-bnr|kaligraf}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[nexşnivîs]], [[spehînivîser]] # {{kontrol|rêz=erê|[[kalîgrafer]]}} ==== Tewandin ==== {{ku-tewîn-nav|mê}} fssqd48wulvujpxqebc3fl2l95ty7ut kalîgrafer 0 344216 6793974 6309537 2026-10-14T18:21:06Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793974 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[nexşnivîs]], [[spehînivîs]], [[kalîgraf]] ==== Tewandin ==== {{ku-tewîn-nav/nû|mê}} gdd5f0evwm04xvf2ikgpyx0qxr64i8i 6794012 6793974 2026-10-14T18:43:31Z Wikihez 7344 Lînkên ser [[spehînivîs]] tê rakirin (via JWB) 6794012 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[nexşnivîs]], [[kalîgraf]] ==== Tewandin ==== {{ku-tewîn-nav/nû|mê}} 547nfdvbtjw498f6e8npz2d4057oc1b 6794061 6794012 2026-10-14T20:12:17Z Balyozbot 7867 Bot: Paqijiyên kozmetîk ({{ku-tewîn-nav/nû}} → {{ku-tewîn-nav}}.) 6794061 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xetat]], [[nexşnivîs]], [[kalîgraf]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} tj415tixwx6955ilzad3ps1tjy92yc7 خطاط 0 367412 6794039 5587691 2026-10-14T19:21:45Z Wikihez 7344 /* Erebî */ [[Bikarhêner:Wikihez/templateScript2.js|TS]]: Ji [[en:خطاط|en.wikt]] 6794039 wikitext text/x-wiki == {{ziman|ar}} == === Bilêvkirin === {{ar-pr|خَطَّاط}} ===Navdêr=== {{navdêr|ar|خَطَّاط|z=n}} # {{mijar|ar|pîşe}} {{g|ku|xetat}}, {{g|ku|xweşnivîs}} ==== Etîmolojî ==== {{etîmon|ar|id=xetat|:reh|خ ط ط<id:reh>|tree=1}} Îsmê meslekî ji fiîla {{qal|ar|خَطَّ|t=xet çêkirin}}. ==== Dûnde ==== * {{dû|ku|xetat|deyn=1}} * {{dû|az|xəttat|deyn=1}} * {{dû|ota|خطاط|tr=hattât|deyn=1}} ** {{dû|tr|hattat|deyn=1}} * {{dû|fa|خطاط|deyn=1}} * {{dû|ug|خەتتات|deyn=1}} * {{dû|uz|xattot|deyn=1}} == {{ziman|fa}} == === Navdêr === {{navdêr|fa|tr=}} # [[xetat]], [[xêzkar]] 0e312zilpkrmq2dg8v4hp198zolm12t bedewnivîsî 0 375485 6793970 6609420 2026-10-14T18:20:41Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793970 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[xweşnivîsî]], [[paknivîsî]] ==== Tewandin ==== {{ku-tewîn-nav|mê}} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|bedew|-nivîsî|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|tr}}: {{W+|tr|hattatlık}} {{werger-bin}} jpzzfx6pcop6nfknuevfm0x1e852cjj mezûniyet 0 433638 6794071 6619841 2026-10-14T20:27:08Z Wikihez 7344 /* Navdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794071 wikitext text/x-wiki {{h-bnr|mezuniyet}} == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{navdêr|ku|z=m}} # [[mezûnî]] ==== Etîmolojî ==== {{etîmon|ku|:qertaf|mezûn|-iyet|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|tr}}: {{W+|tr|mezuniyet}} {{werger-bin}} 1woitvxmf6n2bp5pvdb3mamxp4256m9 حساء 0 436879 6793955 4126417 2026-10-14T17:56:30Z Wikihez 7344 /* Erebî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6793955 wikitext text/x-wiki == {{ziman|ar}} == === Bilêvkirin === * {{deng|ar|Ar-حساء.ogg}} === Navdêr === {{navdêr|ar|حَسَاء|c=n}} # {{e|ar|xwarin}} [[şorbe]] h7a3ezdm1se8urmqk57qzg84u8qljvl 6793956 6793955 2026-10-14T17:57:06Z Wikihez 7344 /* Bilêvkirin */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6793956 wikitext text/x-wiki == {{ziman|ar}} == === Bilêvkirin === {{ar-pr|حَسَاء}} * {{deng|ar|Ar-حساء.ogg}} === Navdêr === {{navdêr|ar|حَسَاء|c=n}} # {{e|ar|xwarin}} [[şorbe]] 94fxp0mu167df5opfoip4c3u9tn7f9q Şablon:W/belge 10 517078 6794093 6534300 2026-10-14T21:56:14Z Wikihez 7344 TemplateData for {{W}} (the parameters it takes), used by the template dialog of the translation tools 6794093 wikitext text/x-wiki {{Lua bikartîne|Modul:werger}} == Bikaranîn == <syntaxhighlight lang="wikitext"> {{W|koda zimanî|peyv|zayend|jimar|tr=translîterasyon|cuda=|guman=}} </syntaxhighlight> Wekî {{şl|W+}} tê bikaranîn, li vir nişana derve '''gewr''' derdikevî. * Zayend: ::<code>n</code> = ''[[nêr]]'' ::<code>m</code> û <code>f</code> = ''[[mê]]'' [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] Di wîkiyên din da <code>m</code> (''masculine'') = ''[[nêr]]'' ::<code>nt</code> = ''[[nêtar]]'' ::<code>mn</code> = ''[[mê]]'' & ''[[nêr]]'' ::<code>g</code> = ''giştî'', ''common gender'' * Jimar: ::<code>p</code>= ''[[pirjimar]]'' ::<code>y</code> = ''[[yekjimar]]'' / ''[[kitjimar]]'' * Translîterasyon: <code><nowiki>tr=</nowiki></code> an <code>R=</code> (<small>'''Nîşe:''' <code>R</code> kurtenavê "Romanîzekirin" e (latînî kirin))</small> * cuda: ::<code><nowiki>cuda=</nowiki></code> Werger û navê gotarê li ser Wîkîferhengê li hev neyên (cuda ne) [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] Bi îngîlîzî "dif" :::: Koda wîkiyê: <nowiki>[[peyv|peyva cudayê]]</nowiki> * guman: ::<code><nowiki>guman=</nowiki></code> <code><nowiki>erê</nowiki></code>: gumanek ser vê wergerê heye === Mînak === '''Peyvên mê''' * <code><nowiki>{{W|fr|cuisine}}</nowiki></code> → {{W|fr|cuisine}} * <code><nowiki>{{W|fr|cuisine|m}}</nowiki></code>→ {{W|fr|cuisine|m}} * <code><nowiki>{{W|fr|cuisine|m|y}}</nowiki></code>→ {{W|fr|cuisine|m|y}} * <code><nowiki>{{W|fr|cuisines|m|p}}</nowiki></code>→ {{W|fr|cuisines|m|p}} * <code><nowiki>{{W|fr|cuisines|m|p|guman=erê}}</nowiki></code>→ {{W|fr|cuisines|m|p|guman=erê}} '''Peyvên nêr''' * <code><nowiki>{{W|fr|mur}}</nowiki></code> → {{W|fr|mur}} * <code><nowiki>{{W|fr|mur|n}}</nowiki></code> → {{W|fr|mur|n}} * <code><nowiki>{{W|fr|mur|n|y}}</nowiki></code> → {{W|fr|mur|n|y}} * <code><nowiki>{{W|fr|murs|n|p}}</nowiki></code> → {{W|fr|murs|n|p}} * <code><nowiki>{{W|fr|murs|n|p|guman=erê}}</nowiki></code> → {{W|fr|murs|n|p|guman=erê}} '''Translîterasyon''' * <code><nowiki>{{W|fa|ماه|tr=mah}}</nowiki></code> → {{W|fa|ماه|tr=mah}} * <code><nowiki>{{W|ru|ба́бочка|m|tr=bábočka}}</nowiki></code> → {{W|ru|ба́бочка|f|tr=bábočka}} '''cuda''' * <code><nowiki>{{W|it|amo|cuda=āmō}}</nowiki></code> → {{W|it|amo|cuda=āmō}} ::<code><nowiki>amo</nowiki></code> gotarê ku ser Wîkîferhengê hatiye nivîsandin ::<code><nowiki>cuda=āmō</nowiki></code> wergerê cudayê ye :: Koda wîkiyê: <nowiki>[[amo|āmō]]</nowiki> == Binêre jî == * {{şl|W}} * {{şl|W+}} * {{şl|W-}} * {{şl|werger-ser}} * {{şl|werger-bin}} * {{şl|Z}} * Lîsteya zimanan: *: [[WF:Lîsteya zimanan]] · [[Modul:languages/code to canonical name.json|code to canonical name.json]] · [[MediaWiki:Gadget-translation editor.js/ziman.json|ziman.json]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Wergerên Z bi koda ziman a nediyarkirî]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Rûpelên bi xetayên koda zimanî]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Rûpelên bi şaşî û kêmasî]] == TemplateData == <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string", "aliases": [ "cuda", "dif" ] }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string", "aliases": [ "R" ] }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" }, "guman": { "label": "Guman", "description": "erê: tercime hê ne kontrolkirî ye; li ber wê \"ji kerema xwe kontrol bike\" tê nivîsîn û rûpel dikeve Kategorî:Kontrola wergerê.", "type": "string", "suggestedvalues": [ "erê" ] } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id", "guman" ] } </templatedata> <includeonly> [[Category:Şablonên wergeran]] </includeonly> 0tgw2wchmyrvu3q19fvzjs5jr4plcrm Şablon:W-/belge 10 517115 6794095 6534297 2026-10-14T21:56:24Z Wikihez 7344 TemplateData for {{W}} (the parameters it takes), used by the template dialog of the translation tools 6794095 wikitext text/x-wiki {{Lua bikartîne|Modul:werger}} == Bikaranîn == Ji bo peyvên ku wîkiyên din da gotarek '''tune ye''': nişana derve dernakevî. Wekî {{şl|W+}} û {{şl|W}} tê bikaranîn, <syntaxhighlight lang="wikitext"> {{W-|koda zimanî|peyv|zayend|jimar|tr=translîterasyon|cuda=|guman=}} </syntaxhighlight> * Zayend: ::<code>n</code> = ''[[nêr]]'' ::<code>m</code> û <code>f</code> = ''[[mê]]'' [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] Di wîkiyên din da <code>m</code> (''male'') = ''[[nêr]]'' ::<code>nt</code> = ''[[nêtar]]'' ::<code>mn</code> = ''[[mê]]'' & ''[[nêr]]'' ::<code>c</code> = ''common genger'' * Jimar: ::<code>p</code> = ''[[pirjimar]]'' ::<code>y</code> = ''[[yekjimar]]'' / ''[[kitjimar]]'' * Translîterasyon: <code><nowiki>tr=</nowiki></code> an <code>R=</code> (<small>'''Nîşe:''' <code>R</code> kurtenavê "Romanîzekirin" e (latînî kirin))</small> * cuda: ::<code><nowiki>cuda=</nowiki></code> Werger û navê gotarê li ser Wîkîferhengê li hev neyên (cuda ne). [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] Bi îngîlîzî "dif" :::: Koda wîkiyê: <nowiki>[[peyv|peyva cudayê]]</nowiki> * guman: ::<code><nowiki>guman=</nowiki></code> <code><nowiki>erê</nowiki></code>: gumanek ser vê wergerê heye === Mînak === '''Peyvên mê''' * <code><nowiki>{{W-|fr|cuisine}}</nowiki></code> → {{W-|fr|cuisine}} * <code><nowiki>{{W-|fr|cuisine|m}}</nowiki></code>→ {{W-|fr|cuisine|m}} * <code><nowiki>{{W-|fr|cuisine|m|y}}</nowiki></code>→ {{W-|fr|cuisine|m|y}} * <code><nowiki>{{W-|fr|cuisines|m|p}}</nowiki></code>→ {{W-|fr|cuisines|m|p}} * <code><nowiki>{{W-|fr|cuisines|m|p|guman=erê}}</nowiki></code>→ {{W-|fr|cuisines|m|p|guman=erê}} '''Peyvên nêr''' * <code><nowiki>{{W-|fr|mur}}</nowiki></code> → {{W-|fr|mur}} * <code><nowiki>{{W-|fr|mur|n}}</nowiki></code> → {{W-|fr|mur|n}} * <code><nowiki>{{W-|fr|mur|n|y}}</nowiki></code> → {{W-|fr|mur|n|y}} * <code><nowiki>{{W-|fr|murs|n|p}}</nowiki></code> → {{W-|fr|murs|n|p}} * <code><nowiki>{{W-|fr|murs|n|p|guman=erê}}</nowiki></code> → {{W-|fr|murs|n|p|guman=erê}} '''Translîterasyon''' * <code><nowiki>{{W-|fa|ماه|tr=mah}}</nowiki></code> → {{W-|fa|ماه|tr=mah}} * <code><nowiki>{{W-|ru|ба́бочка|m|tr=bábočka}}</nowiki></code> → {{W-|ru|ба́бочка|f|tr=bábočka}} '''cuda''' * <code><nowiki>{{W-|it|amo|cuda=āmō}}</nowiki></code> → {{W-|it|amo|cuda=āmō}} == Binêre jî == * {{şl|W}} * {{şl|W+}} * {{şl|W-}} * {{şl|werger-ser}} * {{şl|werger-bin}} * {{şl|Z}} * Lîsteya zimanan: *: '''[[Modul:languages/code to canonical name.json]]''' *: '''[[MediaWiki:Gadget-translation editor.js/ziman.json]]''' *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Wergerên Z bi koda ziman a nediyarkirî]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Rûpelên bi xetayên koda zimanî]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Rûpelên bi şaşî û kêmasî]] == TemplateData == <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string", "aliases": [ "cuda", "dif" ] }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string", "aliases": [ "R" ] }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" }, "guman": { "label": "Guman", "description": "erê: tercime hê ne kontrolkirî ye; li ber wê \"ji kerema xwe kontrol bike\" tê nivîsîn û rûpel dikeve Kategorî:Kontrola wergerê.", "type": "string", "suggestedvalues": [ "erê" ] } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id", "guman" ] } </templatedata> <includeonly> [[Category:Şablonên wergeran]] </includeonly> 23jhhbuh6lz79yl30v8ptvt82kzylr1 Şablon:W+/belge 10 517116 6794094 6534294 2026-10-14T21:56:19Z Wikihez 7344 TemplateData for {{W}} (the parameters it takes), used by the template dialog of the translation tools 6794094 wikitext text/x-wiki {{Lua bikartîne|Modul:werger}} == Bikaranîn == Ji bo peyvên ku wîkiyên din da gotarek '''heye''': nişana derve '''şîn''' derdikevî. Wekî {{şl|W-}} û {{şl|W}} tê bikaranîn. <syntaxhighlight lang="wikitext"> {{W+|koda zimanî|peyv|zayend|jimar|tr=translîterasyon|cuda=|guman=}} </syntaxhighlight> * Zayend: ::<code>n</code> = ''[[nêr]]'' ::<code>m</code> û <code>f</code> = ''[[mê]]'' [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] Di wîkiyên din da <code>m</code> (''male'') = ''[[nêr]]'' ::<code>nt</code> = ''[[nêtar]]'' ::<code>mn</code> = ''[[mê]]'' & ''[[nêr]]'' ::<code>c</code> = ''common genger'' * Jimar: ::<code>p</code>= ''[[pirjimar]]'' ::<code>y</code> = ''[[yekjimar]]'' / ''[[kitjimar]]'' * Translîterasyon: <code><nowiki>tr=</nowiki></code> an <code>R=</code> (<small>'''Nîşe:''' <code>R</code> kurtenavê "Romanîzekirin" e (latînî kirin))</small> * cuda: ::<code><nowiki>cuda=</nowiki></code> Werger û navê gotarê li ser Wîkîferhengê li hev neyên (cuda ne). [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] Bi îngîlîzî "dif" :::: Koda wîkiyê: <nowiki>[[peyv|peyva cudayê]]</nowiki> * guman: ::<code><nowiki>guman=</nowiki></code> <code><nowiki>erê</nowiki></code>: gumanek ser vê wergerê heye === Mînak === '''Peyvên mê''' * <code><nowiki>{{W+|fr|cuisine}}</nowiki></code> → {{W+|fr|cuisine}} * <code><nowiki>{{W+|fr|cuisine|m}}</nowiki></code>→ {{W+|fr|cuisine|m}} * <code><nowiki>{{W+|fr|cuisine|m|y}}</nowiki></code>→ {{W+|fr|cuisine|m|y}} * <code><nowiki>{{W+|fr|cuisines|m|p}}</nowiki></code>→ {{W+|fr|cuisines|m|p}} * <code><nowiki>{{W+|fr|cuisines|m|p|guman=erê}}</nowiki></code>→ {{W+|fr|cuisines|m|p|guman=erê}} '''Peyvên nêr''' * <code><nowiki>{{W+|fr|mur}}</nowiki></code> → {{W+|fr|mur}} * <code><nowiki>{{W+|fr|mur|n}}</nowiki></code> → {{W+|fr|mur|n}} * <code><nowiki>{{W+|fr|mur|n|y}}</nowiki></code> → {{W+|fr|mur|n|y}} * <code><nowiki>{{W+|fr|murs|n|p}}</nowiki></code> → {{W+|fr|murs|n|p}} * <code><nowiki>{{W+|fr|murs|n|p|guman=erê}}</nowiki></code> → {{W+|fr|murs|n|p|guman=erê}} '''Translîterasyon''' * <code><nowiki>{{W+|fa|ماه|tr=mah}}</nowiki></code> → {{W+|fa|ماه|tr=mah}} * <code><nowiki>{{W+|ru|ба́бочка|m|tr=bábočka}}</nowiki></code> → {{W+|ru|ба́бочка|f|tr=bábočka}} '''cuda''' * <code><nowiki>{{W+|it|amo|cuda=āmō}}</nowiki></code> → {{W+|it|amo|cuda=āmō}} == Binêre jî == * {{şl|W}} * {{şl|W+}} * {{şl|W-}} * {{şl|werger-ser}} * {{şl|werger-bin}} * {{şl|Z}} * Lîsteya zimanan: *:[[WF:Lîsteya zimanan]] · [[Modul:languages/code to canonical name.json|code to canonical name.json]] · [[MediaWiki:Gadget-translation editor.js/ziman.json|ziman.json]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Wergerên Z bi koda ziman a nediyarkirî]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Rûpelên bi xetayên koda zimanî]] *: [[Wêne:Zeichen_101_-_Gefahrstelle%2C_StVO_1970.svg|18px|link=]] [[:Kategorî:Rûpelên bi şaşî û kêmasî]] == TemplateData == <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string", "aliases": [ "cuda", "dif" ] }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string", "aliases": [ "R" ] }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" }, "guman": { "label": "Guman", "description": "erê: tercime hê ne kontrolkirî ye; li ber wê \"ji kerema xwe kontrol bike\" tê nivîsîn û rûpel dikeve Kategorî:Kontrola wergerê.", "type": "string", "suggestedvalues": [ "erê" ] } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id", "guman" ] } </templatedata> <includeonly> [[Category:Şablonên wergeran]] </includeonly> h1797is7aemmhzm5e3z4hm3xvw4p4gb chemotherapy 0 637459 6794008 6682175 2026-10-14T18:40:32Z Wikihez 7344 Îngilîzî/Navdêr: mane hate guhartin (DefSideBoxes) 6794008 wikitext text/x-wiki == {{ziman|en}} == === Bilêvkirin === * {{deng|en|LL-Q1860 (eng)-Vealhurl-chemotherapy.wav|Deng|dever=Başûrê Îngilistanê, QY}} === Navdêr === {{wp|ziman=en}} {{en-navdêr|~}} # {{f|en|bijîşkî}} [[kemoterapî]] {{îzah|[[dermankirina|dermankirina]] [[kîmyayî|kîmyayî]]}} ==== Jê ==== * {{g|en|chemotherapeutic}} ==== Etîmolojî ==== {{etîmon|en|:qertaf|chemo-|therapy|metn=++|dar=1}} ewwx1fju0igu0mg31bpqcqffukwvqho 6794062 6794008 2026-10-14T20:12:20Z Balyozbot 7867 Bot: Paqijiyên kozmetîk (Lînk paqij kir.) 6794062 wikitext text/x-wiki == {{ziman|en}} == === Bilêvkirin === * {{deng|en|LL-Q1860 (eng)-Vealhurl-chemotherapy.wav|Deng|dever=Başûrê Îngilistanê, QY}} === Navdêr === {{wp|ziman=en}} {{en-navdêr|~}} # {{f|en|bijîşkî}} [[kemoterapî]] {{îzah|[[dermankirina]] [[kîmyayî]]}} ==== Jê ==== * {{g|en|chemotherapeutic}} ==== Etîmolojî ==== {{etîmon|en|:qertaf|chemo-|therapy|metn=++|dar=1}} de6c8uqfn63dwk6vklsory4pkjtbl36 xetxweş 0 652840 6793978 6680513 2026-10-14T18:21:20Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793978 wikitext text/x-wiki == {{ziman|ku}} == === Rengdêr === {{rengdêr|ku}} # [[başnivîs]], [[xetat]], [[xoşnivîs]] # [[spehînivîs]], [[kalîgrafer]], [[kes]]ê ko [[xet]]ê wî [[ciwan]]e ==== Formên alternatîv ==== * {{g|ku|xetxoş}} ==== Jê ==== * {{g|ku|xetxweşî}} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|xet|xweş|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|ar}}: {{W-|ar|خطاط}} * {{Z|en}}: {{W+|en|calligrapher}} {{werger-bin}} kc48513cp9tip3gtoftwavt4er09r8s 6794015 6793978 2026-10-14T18:43:39Z Wikihez 7344 Lînkên ser [[spehînivîs]] tê rakirin (via JWB) 6794015 wikitext text/x-wiki == {{ziman|ku}} == === Rengdêr === {{rengdêr|ku}} # [[başnivîs]], [[xetat]], [[xoşnivîs]] # [[kalîgrafer]], [[kes]]ê ko [[xet]]ê wî [[ciwan]]e ==== Formên alternatîv ==== * {{g|ku|xetxoş}} ==== Jê ==== * {{g|ku|xetxweşî}} ==== Etîmolojî ==== {{etîmon|ku|:qertaf|xet|xweş|metn=++|dar=1}} ==== Werger ==== {{werger-ser}} * {{Z|ar}}: {{W-|ar|خطاط}} * {{Z|en}}: {{W+|en|calligrapher}} {{werger-bin}} dttkbyayta8jnjk8ldncctwb90qlp20 başnivîs 0 652845 6793964 6264026 2026-10-14T18:20:17Z Wikihez 7344 Lînkên ser [[bedewnivîs]] tê rakirin (via JWB) 6793964 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === [[Wêne:Calligrapher Yiheyuan.jpg|thumb|başnivîs]] {{navdêr|ku|z=mn}} # [[xetat]], [[xetxweş]], [[spehînivîs]], [[kalîgrafer]], [[kes]]ê ku [[xet]]ê wî [[ciwan]]e ==== Bi alfabeyên din ==== * {{ku-ar|باشنڤیس}} ==== Formên alternatîv ==== * [[başnivês]] ==== Têkildar ==== * [[nivîser]] * [[katib]] ==== Etîmolojî ==== [[baş]] + [[nivîs]] ji lêkera ''[[nivîsîn]]'' ==== Jê ==== * [[başnivîsî]] ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Kalligraph}} * {{Z|ar}}: {{W-|ar|الخطاط}} * {{Z|fa}}: {{W+|fa|خطاط}}, {{W-|fa|خوش نویس}} * {{Z|fr}}: {{W+|fr|calligraphe}} * {{Z|en}}: {{W+|en|calligrapher}} {{werger-bin}} qtcq3vieoyel7nast5bqtb121v38uqy 6794009 6793964 2026-10-14T18:41:12Z Balyozbot 7867 [[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan 6794009 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === [[Wêne:Calligrapher Yiheyuan.jpg|thumb|başnivîs]] {{navdêr|ku|z=mn}} # [[xetat]], [[xetxweş]], [[spehînivîs]], [[kalîgrafer]], [[kes]]ê ku [[xet]]ê wî [[ciwan]]e ==== Bi alfabeyên din ==== * {{ku-ar|باشنڤیس}} ==== Formên alternatîv ==== * [[başnivês]] ==== Jê ==== * [[başnivîsî]] ==== Têkildar ==== * [[nivîser]] * [[katib]] ==== Etîmolojî ==== [[baş]] + [[nivîs]] ji lêkera ''[[nivîsîn]]'' ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Kalligraph}} * {{Z|ar}}: {{W-|ar|الخطاط}} * {{Z|fa}}: {{W+|fa|خطاط}}, {{W-|fa|خوش نویس}} * {{Z|fr}}: {{W+|fr|calligraphe}} * {{Z|en}}: {{W+|en|calligrapher}} {{werger-bin}} 9mcvkcv84qhrrpxyun42y13er0emnta 6794010 6794009 2026-10-14T18:43:19Z Wikihez 7344 Lînkên ser [[spehînivîs]] tê rakirin (via JWB) 6794010 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === [[Wêne:Calligrapher Yiheyuan.jpg|thumb|başnivîs]] {{navdêr|ku|z=mn}} # [[xetat]], [[xetxweş]], [[kalîgrafer]], [[kes]]ê ku [[xet]]ê wî [[ciwan]]e ==== Bi alfabeyên din ==== * {{ku-ar|باشنڤیس}} ==== Formên alternatîv ==== * [[başnivês]] ==== Jê ==== * [[başnivîsî]] ==== Têkildar ==== * [[nivîser]] * [[katib]] ==== Etîmolojî ==== [[baş]] + [[nivîs]] ji lêkera ''[[nivîsîn]]'' ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Kalligraph}} * {{Z|ar}}: {{W-|ar|الخطاط}} * {{Z|fa}}: {{W+|fa|خطاط}}, {{W-|fa|خوش نویس}} * {{Z|fr}}: {{W+|fr|calligraphe}} * {{Z|en}}: {{W+|en|calligrapher}} {{werger-bin}} lflygndknyeplyntbdpxco75eanp0k5 بێدەنگ 0 842231 6794006 4736334 2026-10-14T18:31:20Z Wikihez 7344 /* Kurdiya başûrî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6794006 wikitext text/x-wiki == {{ziman|sdh}} == === Navdêr === {{navdêr|sdh}} # {{f|sdh|rêziman}} [[bêdeng]], [[dengdar]] == {{ziman|ckb}} == === Navdêr === {{navdêr|ckb}} # {{f|ckb|rêziman}} [[bêdeng]], [[dengdar]] o3obbg4f6gqud3iiq1sx686dyba68r9 Wîkîferheng:Daxwazên peyitandinê 4 843283 6793979 6724619 2026-10-14T18:23:05Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina 6 madeyan li [[Gotûbêj:pofêldang]], [[Gotûbêj:kîmyaçare]], [[Gotûbêj:hinavbînî]], [[Gotûbêj:inyennegirî]], [[Gotûbêj:kemoapoîzm]], [[Gotûbêj:katdirêj]] 6793979 wikitext text/x-wiki {{/destpêk}} = Hezîran 2020 = == [[romî#Navdêr|romî]] == Li Şingalê maneya polîs? --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 08:00, 15 hezîran 2020 (UTC) = Tîrmeh 2020 = == [[kêlb#p-mane|kêlb]] == --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:24, 22 tîrmeh 2020 (UTC) == [[ejî#p-mane|ejî]] == --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:24, 22 tîrmeh 2020 (UTC) == [[tutirk#peyitandin|tutirk]] == --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:02, 22 tîrmeh 2020 (UTC) :{{r|Guherto}} Niha çawa ye? Bi ya min çavkaniyên ku min dane têrê me dike. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:43, 22 tîrmeh 2020 (UTC) ::{{r|Balyozxane}} Nizanim.. li hin deveran wek tutirk hatîye nivîsandin û li hin jî wek tûtirk. Li hin deveran bi wateya tûşêmî (Rubus) ye, hin deveran jî bi wateya tûerd (Fragaria × ananassa) e û carna jî bi wateya tû (ê spî û reş go li ser daran çê dibin) ye. -- [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 08:16, 23 tîrmeh 2020 (UTC) == [[erbane#p-mane|erbane]] == Ji bo ''Amûreke tenê tekerekî wê heye ji bo veguhestina tiştan wek: axê, kevir, hwd'' ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]''--[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:42, 31 tîrmeh 2020 (UTC) = Tebax 2020 = == [[cifta berazan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:57, 9 tebax 2020 (UTC) == [[tê gerîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:26, 10 tebax 2020 (UTC) == [[tapor]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 20:30, 22 tebax 2020 (UTC) :{{r|Mihemed Qers}} -- [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 22:16, 22 tebax 2020 (UTC) == [[şimzare]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:01, 26 tebax 2020 (UTC) == [[bîrkok]] == Tenê Mehmed Uzun bi kar tîne. Ji bilî kitêbên wî çavkaniyeke din tune. —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 20:38, 28 tebax 2020 (UTC) == [[hunera axaftinê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:46, 30 tebax 2020 (UTC) == [[derrijîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 07:38, 31 tebax 2020 (UTC) == [[yekal]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 09:19, 31 tebax 2020 (UTC) == [[av û agir]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:50, 31 tebax 2020 (UTC) : Di çavkaniyeke de wek av û agir kirin derbas dibe û ji wergerandinên inglîzî cuda ye lê nêzîk e.<br> {{jêder-pirtûk|ku|jêgirtin=weke gelek hunermendên ko dû re agir bera berhêm xwe didin Xwedê jî ji bilî maltaba Nûh tevayiyê vê berhema xwe '''av û agir dike''', van melekên xwe yên doxînsist dikişîne asîmanan, li wir wan tawanbar dike, mîna ko bibêje: de biçin û weke xwe bikin, bi derew û tevgerên xwe yên qirêj dijminiya min bikin, Xwedê wan ji asimanan de serjêr davêje xwarê.|nivîskar=Hesenê Metê|sernav=Labîrenta cinan|weşanxane=Nefel|sal=2002|rûpel=132}} --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:37, 31 tebax 2020 (UTC) == [[elm#p-mane-notice|elm]] == Wateya: ''[[ewlebûn]], sihê pê zanîn, baş pê zanîn''. ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:31, 31 tebax 2020 (UTC) :{{r|Hakîmo}} Ku çavkanî tunebûn em ê jê bibin. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:47, 19 îlon 2020 (UTC) ::{{r|Balyozxane}} Mala te neketê... Hingê bixwe here lê bigere. Jixwe ez reşwî me. Ma eve ne çavkanî ye? -- [[Bikarhêner:Hakîmo|Hakîmo]] ([[Gotûbêja bikarhêner:Hakîmo|gotûbêj]]) 14:48, 19 îlon 2020 (UTC) :::{{r|Hakîmo}} Hinek bixwîne [[Wîkîferheng:Pîvekên tevlîkirinê]]. Jixwe dema ti kesan din çavkanî lê zêde nakin, ez lê digerim û zêde dikim. Na tu ne ti çavkanî yî. Çavkaniyên nivîskî hewce ne. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:51, 19 îlon 2020 (UTC) ::::{{r|Balyozxane}} Erê, lê ku tine bin em çawa bikinî? Em hema jê bibinî? -- [[Bikarhêner:Hakîmo|Hakîmo]] ([[Gotûbêja bikarhêner:Hakîmo|gotûbêj]]) 14:52, 19 îlon 2020 (UTC) :::::{{r|Hakîmo}} Erê em jê dibin. Lê peyvên jê hatine birin em li rûpela [[Pêvek:Bêçavkanî]] zêde dikin. bnr. [[:Kategorî:Pêvek bêçavkanî]]. Dîsa jî ez jî ji vê rewşa me aciz im û bi ya min peyvên devokî divê nayên jêbirin. Lê ji ber ku [[Wîkîferheng:Rêbazname]] dibêje divê werin jêbirin, ez jê dibim. Hevalên {{r|Jiju|Guherto|Ghybu}} [https://ku.wiktionary.org/wiki/Got%C3%BBb%C3%AAj:kejm%C3%BBn], [https://ku.wiktionary.org/wiki/Got%C3%BBb%C3%AAja_p%C3%AAvek%C3%AA:B%C3%AA%C3%A7avkan%C3%AE] jî ji vê rewşê aciz in. Lê ti kes gotûbêj nadin destpêkirin. Ku tu li vir [[Gotûbêja Wîkîferhengê:Pîvekên tevlîkirinê]] gotûbêjek bidî destpêkirin, ez ê piştgirî bidime te. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:58, 19 îlon 2020 (UTC) ::::::Ji kerema xwe min şaş fêm mekin feqet hemû bikarhêner ku tên vira dilpak nînin. Em çito ew peyvan kotrol bikinê? Mixabin, ji bo konrolkirina peyvan di destên me de tenê xwastina çavkaniyan hene amûrek din tine ye... PS: Herwiha me li ser wîkîferhengê Namespace'yên Nimînok ([[Nimînok:Kurdî/serhedî]]?, [[Nimînok:Kurdî/badînî]]?, ..) û Portal ([[Portal:Kurdî]], [[Portal:Frensî]] (mk. [[:fr:Portail:Français]]), ...) jî çêkiriye...--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 19 îlon 2020 (UTC) :::::::Nizanim we nivîsa vira xwand, [[Wîkîferheng:Dîwan#Skotî]]. Piraniya peyvên wîkiya skotî çêkirî ne...--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 19 îlon 2020 (UTC) ::::::::@Ghybu erê min ew xwendibû. Ku em peyvan nepeyitînin, rewşa me jî wek mg.wikt û wikiya skotiyan be. Temam em peyvên devokî jê nebin lê em wan jî nexin werger û rûpelên din heta ku werin peyitandin. Herwiha tu dibêjî amûrek tune ye lê tenê ji bo peyvên devokî û navên heywan/riwekan wisa ye. Gelek caran peyitandin pir hêsan e. Mînak @Hakîmo yan jî hevalên din dikarin blogan binivîsin daku em karibin peyvên wan yên devokî (nepeyitandî) têxin rûpela [[Wîkîferheng:Tomarkirina peyvên ji wêjeya kurdî|Jêgirtinekê tomar bike]] û kengî ew peyv ji hêla nivîskar û blogvanên kurd yên din ve hatine bikaranîn hingê em dikarin li wîkîferhengê jî zêde bikin. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:05, 19 îlon 2020 (UTC) {{outdent|::::::::}}{{r|Hakîmo|Ghybu}} Xêra dê û bavên we '''[[Gotûbêja_Wîkîferhengê:Pîvekên_tevlîkirinê#Li_ser_peyvên_devokî|li vir]]''' fikrên xwe binivîsin daku kesên din bizanin kesên din jî hene vê fikrê min dipejirînin. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 06:48, 3 çiriya pêşîn 2020 (UTC) == [[nîşanbar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 23:51, 31 tebax 2020 (UTC) = Îlon 2020 = == [[besme]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 11:26, 1 îlon 2020 (UTC) == [[ferengi]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:12, 1 îlon 2020 (UTC) == [[çelape]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:22, 1 îlon 2020 (UTC) == [[cesasandin]] == Çavkaniyek klasîk heye, yên din heye gelo? —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 09:02, 2 îlon 2020 (UTC) == [[depan]] == Depan guhertoyeke textan an textîn e. Lê li vir çi mane heye min fêm nekir. —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 10:33, 2 îlon 2020 (UTC) == [[li rûyê dinê xweş mayîn]] == Çavkaniyeke klasîk heye —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 20:38, 2 îlon 2020 (UTC) == [[bêsewî]] == klasîk —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 06:34, 4 îlon 2020 (UTC) == [[merkemoş]] == Klasîk —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 06:34, 4 îlon 2020 (UTC) == [[firfiroka bayî]] == {{r|Mamoste Dilovan}} ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:57, 6 îlon 2020 (UTC) == [[guhê fîlî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:56, 7 îlon 2020 (UTC) : {{r|Bikarhêner}} Min guh li evê peyvê bû ji kesekî ku xudan xwaringehek û baxçeyekî mezin e. : Gelek tişt di berbelav in û xelk dizanin, lê di pertûkan de naye dîtin. {{bêîmze|IbrahimKocher Duhok}} == [[gurovî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:41, 14 îlon 2020 (UTC) :{{r|Bikarhêner}} Çavkanîyên peyvên ku min nivîsandin mixabin ne nivîskî ne. Ev peyv min ji hin mezinên malbatê bihîstine û min jî gelek caran dîtîne ku hatine bikaranîn. Ji bo ku ev peyv wûnda nebin min hem li ba xwe û hem jî li wîkîferhengê nivîsand. Ji bo peyvên ku çavkanîyên nivîskî tunin, mixabin gotinên devkî tene weke çavkanî dimînin. Pêşnîyara te ji bo pevên bi vî awayî çi ye gelo? Spas{{bêîmze|Xwende}} ::{{r|Xwende}} Pêşî li ser malpera [https://tirsik.net/xerhati.php] dûre jî li ser arsiv.org qeyd bike ku nikarî çavkanî peyda bikî. Herwiha bnr [[Alîkarî:Îmze]] --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:00, 15 îlon 2020 (UTC) == [[şîloyî]] == {{r|Xwende}} ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:44, 14 îlon 2020 (UTC) == [[dever]] == Ji bo wateya ''derence, sêlim û merdîwen'' [[Wîkîferheng:Pîvekên tevlîkirinê|çavkanî hewce ye]]. —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:53, 14 îlon 2020 (UTC) == [[kerrnizî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 15:56, 15 îlon 2020 (UTC) == [[li dora (yekê) gerîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:46, 15 îlon 2020 (UTC) == [[glas]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]. --[[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 12:45, 16 îlon 2020 (UTC) == [[kejmûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:42, 17 îlon 2020 (UTC) == [[lirêgerîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:20, 18 îlon 2020 (UTC) == [[çavpeqî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:23, 23 îlon 2020 (UTC) :http://www.geocities.ws/mehname2004a/63/pexsan5.html -- [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 16:58, 23 îlon 2020 (UTC) == [[çavpeqiyayî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:14, 23 îlon 2020 (UTC) :{{r|Balyozxane}} Çavpeqî û -iyayî eynî peyv in, heke bo nimûne 2 çavkanî ji bo çavpeqî û 1 ji bo -iyayî werin dîtin ew weke 3 ji bo yek peyv tên jimartin? -- [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:16, 23 îlon 2020 (UTC) ::{{r|Guherto}}. Na, divê yek ji wan peyitandî be daku em karibin yek ji wan wek guherto nîşan bidin. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:45, 23 îlon 2020 (UTC) :::Temam naxwe. {{=(}} -- [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:48, 23 îlon 2020 (UTC) ::::Ecêb e peyva çavpeqîn jî tune ye. Divê çavpeqî/çavpeqiyayî jê hatibe lê qet tune. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:56, 23 îlon 2020 (UTC) :::::Em çavpeqî ne weke kesekî ku çavê wan peqî ye dibêjin. Siqêfek e. ''Wilo neke, çavpeqî!'' -- [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 18:10, 23 îlon 2020 (UTC) == [[olfatûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:58, 24 îlon 2020 (UTC) :Du çavkanî hatiye dîtin li hêviya ya sisêyan e. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:46, 23 sibat 2025 (UTC) == [[hekûke]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:00, 28 îlon 2020 (UTC) :Tenê çavkaniyek ji Kovara Kurmancî heye ew jî ji kitêba Gundê Mêrxasan a Eliyê Evdilrehman hatiye girtin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:49, 23 sibat 2025 (UTC) == [[dê bêjî kaya di serî de]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:58, 29 îlon 2020 (UTC) == [[heçî ne li malekê, nizanit halekê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:58, 29 îlon 2020 (UTC) == [[heta nîva destî reş nebit, tama devî xoş nabit]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:58, 29 îlon 2020 (UTC) == [[kirtop]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Herwiha [[kirtope]]—[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:59, 29 îlon 2020 (UTC) : == [[pişkûmişk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 21:25, 29 îlon 2020 (UTC) = Çiriya pêşîn 2020 = == [[ma te şulê pê ye?]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:19, 1 çiriya pêşîn 2020 (UTC) == [[zelam yêt heyn û zelamoşk jî yêt heyn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:28, 1 çiriya pêşîn 2020 (UTC) == [[ezîz î&#59; ji pêrve ne mîzî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:15, 1 çiriya pêşîn 2020 (UTC) == [[qelpez]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 08:19, 6 çiriya pêşîn 2020 (UTC) == [[firêzer]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 15:27, 6 çiriya pêşîn 2020 (UTC) == [[kirmişk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 09:03, 7 çiriya pêşîn 2020 (UTC) == [[orijîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 19:03, 7 çiriya pêşîn 2020 (UTC) == [[şequdeq]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 08:50, 9 çiriya pêşîn 2020 (UTC) == [[helizandin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 11:23, 9 çiriya pêşîn 2020 (UTC) == [[rewastin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 11:48, 9 çiriya pêşîn 2020 (UTC) == [[xunaftin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:31, 9 çiriya pêşîn 2020 (UTC) == [[itîmad li ser kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 16:32, 15 çiriya pêşîn 2020 (UTC) == [[sengesor]] == {{r|Saberabdollahizad}} ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:00, 16 çiriya pêşîn 2020 (UTC) :Navçeya Mirgewerê, Ûrmîye. {{bêîmze|Saberabdollahizad}} == [[zenek]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:03, 16 çiriya pêşîn 2020 (UTC) :{{r|Saberabdollahizad}} [[zenek]] jî li Ûrmiyê tê bikaranîn? --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 19:53, 18 çiriya pêşîn 2020 (UTC) == [[pind#p-mane|pind]] == Min tenê çavkaniyek dît ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:48, 17 çiriya pêşîn 2020 (UTC) :Navçeya Mirgewerê, Ûrmîye. {{bêîmze|Saberabdollahizad}} == [[maqezer]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:53, 18 çiriya pêşîn 2020 (UTC) :Navçeya Mirgewerê, Ûrmîye. {{bêîmze|Saberabdollahizad}} == [[pezpezok]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:55, 18 çiriya pêşîn 2020 (UTC) :Navçeya Mirgewerê, Ûrmîye. {{bêîmze|Saberabdollahizad}} == [[parapet#p-mane|parapet]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:53, 31 çiriya pêşîn 2020 (UTC) = Çiriya paşîn 2020 = == [[quloç#p-mane|quloç]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:43, 19 çiriya paşîn 2020 (UTC) == [[di ber (yekî) de avêtin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:57, 20 çiriya paşîn 2020 (UTC) :https://tirsik.net/index.php?mijarid=18749&iro=on&rupel=1 —[[Bikarhêner:Mamoste Dilovan|Mamoste Dilovan]] ([[Gotûbêja bikarhêner:Mamoste Dilovan|gotûbêj]]) 17:37, 20 çiriya paşîn 2020 (UTC) :: {{r|Mamoste Dilovan}} [[di ber re avêtin]] heye. lê te wek di ber '''de''' lê zêde kir. Em biguherînin? Gelek çavkanî ji bo di ber re avêtin heye. --[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:41, 20 çiriya paşîn 2020 (UTC) == [[hêvastin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 10:49, 21 çiriya paşîn 2020 (UTC) == [[lamçîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 11:28, 22 çiriya paşîn 2020 (UTC) == [[poz#p-mane|poz]] == Ji bo maneya "[[quz]], [[vajîna]]" ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 11:28, 22 çiriya paşîn 2020 (UTC) == [[sihetbexş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:35, 24 çiriya paşîn 2020 (UTC) == [[hevjêhatin ji jêhatîbûnê çêtir e]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:14, 25 çiriya paşîn 2020 (UTC) == [[gerriyan#p-mane|gerriyan]] == ''Ji bo wateya cereyan (tevger)''. Qasî ku dibînim tenê Cewerî bi kar aniye —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:24, 25 çiriya paşîn 2020 (UTC) == [[derbend]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:08, 30 çiriya paşîn 2020 (UTC) = Kanûna paşîn 2021 = == [[desthelgirtin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:48, 6 kanûna paşîn 2021 (UTC) == [[êşewî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:52, 7 kanûna paşîn 2021 (UTC) == [[sexorr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 20:22, 7 kanûna paşîn 2021 (UTC) == [[sapût]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:06, 12 kanûna paşîn 2021 (UTC) = Çiriya pêşîn 2022 = == [[paşbingeh]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 07:40, 12 çiriya pêşîn 2022 (UTC) = Kanûna paşîn 2023 = == [[berê beringa]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:35, 1 kanûna paşîn 2023 (UTC) == [[mûlûleya xwînê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:36, 6 kanûna paşîn 2023 (UTC) == [[mirovdostî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 05:38, 12 kanûna paşîn 2023 (UTC) == [[derîner]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 23:02, 15 kanûna paşîn 2023 (UTC) == [[neteba]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 00:01, 16 kanûna paşîn 2023 (UTC) :Maneya "uyruksuz" ji ferhenga Farqînî hatiye kopîkirin wekî din bi giştî bi maneya "nakokî" tê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:33, 26 hezîran 2026 (UTC) == [[narkotîkfiroş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 10:59, 16 kanûna paşîn 2023 (UTC) == [[pirik#p-mane|pirik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:15, 16 kanûna paşîn 2023 (UTC) == [[ramyarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ez dibêjim ev peyv tenê bi maneya "fikrî", wek rengdêr heye. Lê belê di soranî de bi şaşemilandineke wekî "siyasî" bi kar anîne, kurmancên me jî rasterast maneya soranî tevlî çend ferhengên hev-kopîkirî kirine û bi vî awayî belkî hin çavkanî hebin. Lê ez dibêjim divê ew çavkanî jî neyên hesibandin. Hin kes zen dikin ku her tiştê di soranî de ripîrast e. Mesele heqeten eletewş e. Agahiyeke li ser peyvê: "[https://www.welateme.info/kurdi/modules.php?name=News&file=print&sid=686 Welateme.info]" Li vir peyv bi maneya "fikrî" hatiye bikaranîn: "[https://xwebun1.org/ziman-sirhelal-e/ Xwebûn]". Qewet be. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:36, 19 kanûna paşîn 2023 (UTC) == [[dolîwgeh]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:08, 22 kanûna paşîn 2023 (UTC) = Sibat 2023 = == [[herebaş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:13, 11 sibat 2023 (UTC) == [[balindeyê korare]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê çavkaniyekê tê xuyan, ew jî kitêba "Balindeyên Kurdistanê" yê Îsmaîl Taha Şahîn e (Îsmaîl Taha Şahîn (2006). Balindeyên Kurdistanê, 1-4. Spîrêz, Duhok). Ev kitêb ji ser înternetê wek word jî peyda dibe. Xuya ye ku ji Weşanên Spîrez fîzîken jî çap bûye. Lê wekî çavkanî tenê ev tê xuyan. —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:10, 14 sibat 2023 (UTC) == [[dîbeyt]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:13, 18 sibat 2023 (UTC) == [[çend pat]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:35, 24 sibat 2023 (UTC) == [[rabînîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ji bo kurmancî bêçavkanî ye. Ger ji bo soranî çavkanî hebin jî divê ji bo soranî bi herfên aramî were nivîsîn. An ger halê latînîkirî ya peyveke soranî be, dîsa divê ev hal were eşkerekirin. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:36, 26 sibat 2023 (UTC) = Adar 2023 = == [[qirar#p-mane|qirar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:23, 2 adar 2023 (UTC) == [[pîşpêkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê di nivîseke online ya Cemal Nebez de derbas dibe. Dibe ku peyveke başûriyan be. Û di wê nivîsa Cemal Nebezê bi forma "pîş bi ... bikin" jî derbas dibe. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:29, 3 adar 2023 (UTC) == [[kûcag]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:27, 27 adar 2023 (UTC) == [[destbizavî kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Xuya ye ku ev peyv tenê di kitêba “''Nameyek Şernexê''” yê Evdirehman Gundikî de derbas bûye. Û ji wir jî Mistefa Aydoganî maneya peyvê di hejmara 61em ya Kovara ''Kurmancî'' de belav kiriye (rûpela 2yem). Lê belê tenê yek çavkaniyekê ne bes e ji bo ku em vê peyvê di vê ferhengê de wekî peyveke çavkanî-temam belav bikin. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:40, 28 adar 2023 (UTC) == [[germav baş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:23, 31 adar 2023 (UTC) == [[germavxweş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:27, 31 adar 2023 (UTC) = Gulan 2023 = == [[kirbend]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 08:34, 5 gulan 2023 (UTC) : Çêkerê rûpelê wek not nivîsiye ku peyv ji herêma Çewlikê ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 06:24, 6 gulan 2023 (UTC) == [[We are off now]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:13, 22 gulan 2023 (UTC) == [[baran ji erd û esmana lê diket]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:46, 26 gulan 2023 (UTC) == [[sîba]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:44, 30 gulan 2023 (UTC) == [[bîrîner]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:20, 30 gulan 2023 (UTC) = Hezîran 2023 = == [[bidestmiz]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:49, 10 hezîran 2023 (UTC) = Tîrmeh 2023 = == [[parzeng]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:01, 15 tîrmeh 2023 (UTC) == [[zerpedest]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:05, 15 tîrmeh 2023 (UTC) == [[heta destên mirov nebine gû, devê mirov nabite tû]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:27, 19 tîrmeh 2023 (UTC) == [[çemê mezin tê dibore diçe]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:38, 19 tîrmeh 2023 (UTC) == [[kevanê nebî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:45, 19 tîrmeh 2023 (UTC) == [[teybûr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê li malpera yuksekovahaber.com-ê derbas dibe, li wir jî bi maneya "çare, çareserî"yê hatiye emilandin, ne bi vê maneyê. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:00, 19 tîrmeh 2023 (UTC) == [[nikêtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:22, 19 tîrmeh 2023 (UTC) = Tebax 2023 = == [[prêze]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 21:19, 15 tebax 2023 (UTC) == [[qelye]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 15:59, 25 tebax 2023 (UTC) = Îlon 2023 = == [[hetwan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 20:21, 14 îlon 2023 (UTC) == [[milyonmasî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:06, 21 îlon 2023 (UTC) == [[tofana mêjî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:20, 21 îlon 2023 (UTC) == [[îshaf]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 10:57, 23 îlon 2023 (UTC) == [[odeya karkirinê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 10:57, 26 îlon 2023 (UTC) == [[odeya şixulînê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 11:42, 26 îlon 2023 (UTC) == [[hîter]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:08, 26 îlon 2023 (UTC) = Çiriya pêşîn 2023 = == [[vîtamîn waw]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 20:20, 1 çiriya pêşîn 2023 (UTC) == [[berhan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:21, 3 çiriya pêşîn 2023 (UTC) == [[sêlikdêr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:16, 10 çiriya pêşîn 2023 (UTC) == [[qewandêr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:20, 10 çiriya pêşîn 2023 (UTC) == [[darav]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 20:04, 11 çiriya pêşîn 2023 (UTC) = Çiriya paşîn 2023 = == [[kansernasî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:54, 14 çiriya paşîn 2023 (UTC) == [[golgir]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 16:06, 15 çiriya paşîn 2023 (UTC) == [[ahardar]] == Tenê Zana Farqînî —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 10:34, 26 çiriya paşîn 2023 (UTC) = Kanûna pêşîn 2023 = == [[Kî mal ezîz bike, can rezîl dike&#59; kî mal rezîl bike, can ezîz dike.]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:55, 2 kanûna pêşîn 2023 (UTC) == [[porsing]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:22, 2 kanûna pêşîn 2023 (UTC) == [[newelatî kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:57, 2 kanûna pêşîn 2023 (UTC) == [[newelatîkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:58, 2 kanûna pêşîn 2023 (UTC) == [[alîkar kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 18:42, 3 kanûna pêşîn 2023 (UTC) == [[aramoş kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'' -- [[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 18:48, 3 kanûna pêşîn 2023 (UTC) == [[bêvil kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 18:56, 4 kanûna pêşîn 2023 (UTC) == [[dijberiya naverokî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 22:43, 8 kanûna pêşîn 2023 (UTC) == [[bersivîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]]'', û her wiha ne li gorî qayideyên kurdî (kurmancî) ye. —[[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 22:08, 11 kanûna pêşîn 2023 (UTC) == [[hevalzewac]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:25, 18 kanûna pêşîn 2023 (UTC) = Kanûna paşîn 2024 = == [[raperk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:40, 15 kanûna paşîn 2024 (UTC) :Bi wê manê nîne. Bi maneke dî heye. [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 19:26, 6 tebax 2024 (UTC) = Sibat 2024 = == [[bi navê lîstin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:02, 3 sibat 2024 (UTC) : Li ser agahdarkirina çêkerê rûpelê me şablona "dever"ê tevlî kir. Peyv ji herêma Qoser, Mêrdînê ye. Me li qismê "Bikaranîn"ê jî vê yekê dîyar kir. Ji xeynî vê me wê peyvê ji rûpelên din jî bir. Bi vî awayî ev made kare were girtin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:28, 10 nîsan 2024 (UTC) == [[preskirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 00:43, 11 sibat 2024 (UTC) == [[pres kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 00:43, 11 sibat 2024 (UTC) == [[Xwedê te têr bike]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:41, 13 sibat 2024 (UTC) == [[isad]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:09, 22 sibat 2024 (UTC) == [[razemendîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:44, 27 sibat 2024 (UTC) = Adar 2024 = == [[zilhêzî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:19, 5 adar 2024 (UTC) == [[hingihîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:32, 13 adar 2024 (UTC) == [[xore]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:30, 15 adar 2024 (UTC) == [[melef]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:55, 19 adar 2024 (UTC) == [[uğursuz ayak]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:11, 23 adar 2024 (UTC) == [[şepûk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:22, 23 adar 2024 (UTC) == [[agirberdêrî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 00:04, 26 adar 2024 (UTC) == [[kîrquz]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:00, 26 adar 2024 (UTC) == [[kîrquzdê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:01, 26 adar 2024 (UTC) == [[quzgêj]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:03, 26 adar 2024 (UTC) = Nîsan 2024 = == [[hîkarîkerî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:53, 1 nîsan 2024 (UTC) == [[hîkarîker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:54, 1 nîsan 2024 (UTC) == [[hîkarîdarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:55, 1 nîsan 2024 (UTC) == [[hîkarîdar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:56, 1 nîsan 2024 (UTC) == [[bihîkarîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:57, 1 nîsan 2024 (UTC) == [[bihîkarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:58, 1 nîsan 2024 (UTC) == [[bêhîkarîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:58, 1 nîsan 2024 (UTC) == [[bêhîkarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:59, 1 nîsan 2024 (UTC) == [[bêhîkarî bûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:00, 1 nîsan 2024 (UTC) == [[bêhîkarî kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:00, 1 nîsan 2024 (UTC) == [[bêhîkarîbûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:01, 1 nîsan 2024 (UTC) == [[bêhîkarîkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:01, 1 nîsan 2024 (UTC) == [[hîkarîkirî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:02, 1 nîsan 2024 (UTC) == [[hîkarîkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:03, 1 nîsan 2024 (UTC) == [[hîkarî kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:07, 1 nîsan 2024 (UTC) == [[rayda]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:40, 1 nîsan 2024 (UTC) == [[bêrayda]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:42, 1 nîsan 2024 (UTC) == [[bêraydayî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:43, 1 nîsan 2024 (UTC) == [[bêraydatî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:43, 1 nîsan 2024 (UTC) == [[bêrayda bûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:43, 1 nîsan 2024 (UTC) == [[bêrayda kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:43, 1 nîsan 2024 (UTC) == [[bêraydabûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:44, 1 nîsan 2024 (UTC) == [[bêraydakirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:44, 1 nîsan 2024 (UTC) == [[birayda]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:44, 1 nîsan 2024 (UTC) == [[biwerêç]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:46, 1 nîsan 2024 (UTC) == [[biwerêçî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:46, 1 nîsan 2024 (UTC) == [[biwerêçîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:47, 1 nîsan 2024 (UTC) == [[biwerêçtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:47, 1 nîsan 2024 (UTC) == [[werêç]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:49, 1 nîsan 2024 (UTC) == [[werêç kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 1 nîsan 2024 (UTC) == [[werêçkirî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 1 nîsan 2024 (UTC) == [[werêçkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:51, 1 nîsan 2024 (UTC) == [[wişto]] == @[[Bikarhêner:Zerwanda]] Çavkanî ji bo kurmancî tine ye. Zazakî ye? —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 20:19, 1 nîsan 2024 (UTC) :ji devera mûşê, aliyê diya min "wişto/wiştik" digotin. [[Bikarhêner:Zerwanda|Zerwanda]] ([[Gotûbêja bikarhêner:Zerwanda|gotûbêj]]) 20:44, 1 nîsan 2024 (UTC) == [[xewgerî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:19, 2 nîsan 2024 (UTC) == [[xewgerokî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:28, 2 nîsan 2024 (UTC) == [[nimêh]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:51, 2 nîsan 2024 (UTC) == [[فعال]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' [[Bikarhêner:Ghybu|Gyhbu]] di 2021an de li ser rûpela wî tevlî kirîye. Lê ji ber nebûna vê sîstema me tevlî vir nebûye. Lewma min anî ser vir jî. Wê gavê tenê ji bo maneya "kar" ev şablon hatîye tevlîkirin. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 07:27, 3 nîsan 2024 (UTC) == [[pêjor]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:46, 6 nîsan 2024 (UTC) == [[hafil]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:48, 6 nîsan 2024 (UTC) == [[micemi]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Muhtemelem şiklê rast wek "micemî" ye, lê ew jî bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:52, 9 nîsan 2024 (UTC) == [[hesaba xwe]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ji xeynî van ev forma wê jî bêçavkanî ne: "[[hisaba xwe]] / [[hesaba xo]] / [[hisaba xo]]". — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:25, 9 nîsan 2024 (UTC) == [[leşkerma]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:53, 24 nîsan 2024 (UTC) = Gulan 2024 = == [[serîi]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Wek "serî" belkî li kar be, lê wekî "serîi" çavkanî peyda nabe. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:20, 2 gulan 2024 (UTC) == [[virgu]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Bêçavkanî tê xuyan. Peyv bi tirkî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:49, 2 gulan 2024 (UTC) == [[xwerre]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:57, 20 gulan 2024 (UTC) = Hezîran 2024 = == [[di nav deryaya peyvan de avjenî dikir]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:34, 4 hezîran 2024 (UTC) == [[berbixwe]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:03, 10 hezîran 2024 (UTC) == [[şipyakî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:08, 10 hezîran 2024 (UTC) :"şipêkî" heye [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 20:20, 27 tîrmeh 2024 (UTC) == [[wetalane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:18, 10 hezîran 2024 (UTC) == [[wetalî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:19, 10 hezîran 2024 (UTC) == [[wetalîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:20, 10 hezîran 2024 (UTC) == [[wetaltî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:20, 10 hezîran 2024 (UTC) == [[se direye karwan dibuhire]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:49, 16 hezîran 2024 (UTC) == [[se direwît, karwan bi rêka xo diçît]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Formeke bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:53, 16 hezîran 2024 (UTC) == [[se direwît, karwan diburît]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Formeke bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:54, 16 hezîran 2024 (UTC) == [[se direyê karwan dibûrê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Formeke bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:55, 16 hezîran 2024 (UTC) == [[se direye karvan li riya xwe diçe]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Formeke bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:56, 16 hezîran 2024 (UTC) == [[se direye û kerwan dibihure]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Formeke bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:00, 16 hezîran 2024 (UTC) == [[hingê dinya]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:07, 18 hezîran 2024 (UTC) == [[berdanberdan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:22, 24 hezîran 2024 (UTC) == [[nîska mala Barzan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:05, 25 hezîran 2024 (UTC) = Tîrmeh 2024 = == [[avmêwe]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:41, 16 tîrmeh 2024 (UTC) == [[nihalok]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:53, 24 tîrmeh 2024 (UTC) == [[nihalk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:54, 24 tîrmeh 2024 (UTC) == [[niwalok]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:54, 24 tîrmeh 2024 (UTC) == [[niwalk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:55, 24 tîrmeh 2024 (UTC) == [[niwalik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:55, 24 tîrmeh 2024 (UTC) == [[nehalok]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:56, 24 tîrmeh 2024 (UTC) == [[nehalk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:56, 24 tîrmeh 2024 (UTC) == [[nehalik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:57, 24 tîrmeh 2024 (UTC) == [[alîkarxwazî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:16, 24 tîrmeh 2024 (UTC) == [[nihalik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:19, 24 tîrmeh 2024 (UTC) == [[zêrîb]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Bi dîtina min, ev form bêçavkanî ye. Dibe ku ji bo forma "zerîb" çavkanî hebe, lê ew form li ser Wîkîferhengê wek made nehatîye çêkirin. Pir kêm be jî ev peyv wek "zerîp" jî li cihekî derbas bûye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:31, 24 tîrmeh 2024 (UTC) == [[qanat]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ev peyv bi maneya "kanal"ê nîne, xelet e. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 06:59, 25 tîrmeh 2024 (UTC) == [[qenat]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ev peyv bi maneya "kanal"ê nîne, xelet e. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 06:59, 25 tîrmeh 2024 (UTC) :Min 1 çavkanî dît, "Ev kes rojê 6-7 qenatên TV ya diğere û diaxive" https://darkamazi.info/archives/1019704 (bi lêgerana "qenatên", ji ber ko lêgeranên dî tijî tiştên dî derdixin) :Lê mimkun e xelet nivîsîbe. [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 20:31, 27 tîrmeh 2024 (UTC) == [[şkaftmend]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 07:08, 25 tîrmeh 2024 (UTC) :3 çavkanî hene. Lê xuya dike jêdera wan hemû ferhenga tirsik.netê e. :* Çavkanî 1: https://tirsik.net/ferheng?query=şkaftmend (berî 2014, lê tarîx nîne) :* Çavkanî 2: https://ku.wikipedia.org/wiki/Deranderî (2011) :* Çavkanî 3: https://x.com/zerdeceno/status/1155331427510247425 (2019) :[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 20:28, 27 tîrmeh 2024 (UTC) == [[dan hev#p-mane|dan hev]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ev mane li ser çavkanîyan peyda nabe. Tenê çêkerê rûpelê vê maneyê nivîsîye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:08, 25 tîrmeh 2024 (UTC) == [[baristdar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:47, 25 tîrmeh 2024 (UTC) == [[baristdarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:48, 25 tîrmeh 2024 (UTC) == [[baristdarîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:49, 25 tîrmeh 2024 (UTC) == [[baristdartî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:50, 25 tîrmeh 2024 (UTC) == [[bibaristî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:51, 25 tîrmeh 2024 (UTC) == [[bibarist]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:52, 25 tîrmeh 2024 (UTC) == [[bibaristîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:53, 25 tîrmeh 2024 (UTC) == [[bêbaristî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:54, 25 tîrmeh 2024 (UTC) == [[bêbarist]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:55, 25 tîrmeh 2024 (UTC) == [[peydabûnîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 20:52, 27 tîrmeh 2024 (UTC) == [[nexşvan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 21:05, 27 tîrmeh 2024 (UTC) == [[nexşvanî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 21:05, 27 tîrmeh 2024 (UTC) == [[lêvikî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 21:47, 27 tîrmeh 2024 (UTC) == [[lêvik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Min zêde nemeyzand. —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 21:48, 27 tîrmeh 2024 (UTC) == [[şerenîx kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 21:51, 27 tîrmeh 2024 (UTC) == [[sala çîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:09, 29 tîrmeh 2024 (UTC) == [[berê pêşîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:57, 30 tîrmeh 2024 (UTC) == [[berê ewilî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:57, 30 tîrmeh 2024 (UTC) = Tebax 2024 = == [[xwîndijmin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:02, 4 tebax 2024 (UTC) == [[reîsîtî]]/[[reysîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 18:17, 6 tebax 2024 (UTC) == [[soranîaxivî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 18:43, 6 tebax 2024 (UTC) == [[helîna bildo]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ji 2020a ve şablon li wir e min tenê li vir zêde kir. —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 19:56, 6 tebax 2024 (UTC) == [[hilbijêrînerî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' "Hilbij" rihê peyvê ye, "-er" jî maneya "kesê dike" dide, qertafa "-îner / -înerî" ya li dawîyê bi eşkereyî xelet e. Û ev peyv bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:45, 7 tebax 2024 (UTC) == [[îqlîmtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:43, 10 tebax 2024 (UTC) == [[jîrbêjî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ev peyv ger di soranî de ji bo wê çavkanîyên têr hebe, tenê wek "Nivîsîna bi herfên latînî ya peyva ..." kare were qeydkirin. Lê nabe ku wek kurmancî be, were qeydkirin. Ji bo kurmancî tenê di meqaleyekê de derbas dibe, ew jî ji alî başûrîyekî ve hatîye nivîsîn. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:55, 10 tebax 2024 (UTC) == [[hîgyen]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê ev form bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 07:40, 12 tebax 2024 (UTC) == [[kolîner]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:45, 12 tebax 2024 (UTC) == [[poşmanî kişîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê di çavkanîyekê de peyda dibe. Ew çavkanîya dayî jî ihtîmal e ku bi tesîra zimanê tirkî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:44, 13 tebax 2024 (UTC) == [[para hevpar a herî mezin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:09, 14 tebax 2024 (UTC) == [[cara hevpar a herî biçûk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:10, 14 tebax 2024 (UTC) == [[regjimar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:16, 14 tebax 2024 (UTC) == [[nîvrengî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:33, 18 tebax 2024 (UTC) == [[nîvdûrgeh]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Wek "guharto" hatiye qeydkirin, lê yek nikare wilo bêje. Ne "guharto" ye, ji ber wê çavkanî hewce ne. —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:39, 18 tebax 2024 (UTC) == [[nîvtewlîsî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:44, 18 tebax 2024 (UTC) == [[nîvrewa]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:44, 18 tebax 2024 (UTC) == [[nîvşehtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:46, 18 tebax 2024 (UTC) == [[nîvfalincî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:46, 18 tebax 2024 (UTC) == [[nîvgilkarî]] == Ferhenga Sa.Sa. ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 17:48, 18 tebax 2024 (UTC) == [[kesîkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 20:16, 18 tebax 2024 (UTC) == [[biryarbirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:38, 24 tebax 2024 (UTC) = Îlon 2024 = == [[qîblename]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 15:15, 1 îlon 2024 (UTC) == [[cihetnima]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ji [https://bianet.org/yazi/ferhenga-ser-u-amrazen-ser-ji-kovara-ronahiye-2-196299 vir] hatiye —[[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 15:27, 1 îlon 2024 (UTC) == [[qûnderîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:36, 7 îlon 2024 (UTC) == [[hevpîşehî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:48, 14 îlon 2024 (UTC) == [[hevpîşeyî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Wek ''navdêr'' bêçavkanî ye. Wek ''rengdêr'' hebe jî, kêmçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:50, 14 îlon 2024 (UTC) == [[hempay]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:04, 14 îlon 2024 (UTC) == [[nîs]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:43, 14 îlon 2024 (UTC) == [[êxbarçî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ji bo ''îxbarçî'' çavkanîyekê peyda dibe, lê kêm e. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:06, 14 îlon 2024 (UTC) == [[muxbîrîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:38, 14 îlon 2024 (UTC) == [[ixbarçî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:43, 14 îlon 2024 (UTC) == [[westgeha pasê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:33, 19 îlon 2024 (UTC) == [[avikmadk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:24, 19 îlon 2024 (UTC) == [[ferwerîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:18, 20 îlon 2024 (UTC) == [[bipayderî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:12, 20 îlon 2024 (UTC) == [[bipayderîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:13, 20 îlon 2024 (UTC) == [[bipaydertî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:13, 20 îlon 2024 (UTC) == [[bipedalî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:14, 20 îlon 2024 (UTC) == [[bipedalîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:14, 20 îlon 2024 (UTC) == [[bipedaltî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:14, 20 îlon 2024 (UTC) == [[pedaldarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:15, 20 îlon 2024 (UTC) == [[pedaldarîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:15, 20 îlon 2024 (UTC) == [[pedaldartî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:16, 20 îlon 2024 (UTC) == [[ciwên]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:11, 22 îlon 2024 (UTC) == [[belgebirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:31, 23 îlon 2024 (UTC) == [[avsîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Dibe ku ev kelîme biçavkanî be, lê pir bêagahî hatîye qeydkirin. Biçavkanî be jî, ji ber qeydkirina bêagahî (li ser madeyê agahî kêm hatine pêşkêşkirin) em rûpelê jê bibin. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:53, 23 îlon 2024 (UTC) :@[[Bikarhêner:Bikarhêner|Bikarhêner]] Ma ne çêtir e li "Daxwazên zelalkirinê" be naxwe? [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 13:40, 10 çiriya paşîn 2024 (UTC) :: {{r|Guherto}} Bi min em karin vê madeyê ji ber bêagahîbûna wî, îlaweyî "Daxwazên jêbirinê" bikin, lê "Daxwazên zelalkirinê" nabe. Wekî din ji ber ku dikeve çarçoveya her du rûpelan jî kîjan be jî dibe, lê madem me li vir da destpêkirin, bila ji vir dewam bike dengdana li ser vê kelîmeyê. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:22, 10 çiriya paşîn 2024 (UTC) == [[kûvank]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Herçiqas tenê formekê be jî, bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:14, 27 îlon 2024 (UTC) == [[kikark]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Herçiqas formekê be jî, bêçavkanî dixuye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:40, 27 îlon 2024 (UTC) == [[kîfkarik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Herçiqas tenê formekê be jî, bêçavkanî dixuye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:45, 27 îlon 2024 (UTC) == [[kuvank]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Herçiqas formekê be jî, bêçavkanî dixuye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:53, 27 îlon 2024 (UTC) == [[kûvarik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Herçiqas tenê formekê be jî, bêçavkanî dixuye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:01, 27 îlon 2024 (UTC) = Çiriya pêşîn 2024 = == [[muhil]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê qismê kurdî bêçavkanî ye, ne ya tirkî. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:00, 9 çiriya pêşîn 2024 (UTC) == [[muhilk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:00, 9 çiriya pêşîn 2024 (UTC) == [[jêrmal]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:59, 15 çiriya pêşîn 2024 (UTC) = Çiriya paşîn 2024 = == [[bîrxistiner]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 19:38, 14 çiriya paşîn 2024 (UTC) == [[wisa gotin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ne ferhengî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 20:13, 15 çiriya paşîn 2024 (UTC) == [[tenzîlatî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:12, 16 çiriya paşîn 2024 (UTC) == [[armancparêz]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:03, 19 çiriya paşîn 2024 (UTC) == [[şikaftmend]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:06, 19 çiriya paşîn 2024 (UTC) == [[derander]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:08, 19 çiriya paşîn 2024 (UTC) == [[deranderî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 13:09, 19 çiriya paşîn 2024 (UTC) == [[derxwenîn]] == @[[User:Keyomerd]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 22:38, 19 çiriya paşîn 2024 (UTC) == [[kiyû]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 23:53, 22 çiriya paşîn 2024 (UTC) == [[goçavik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 00:03, 23 çiriya paşîn 2024 (UTC) == [[hûrhesibaner]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 00:19, 23 çiriya paşîn 2024 (UTC) == [[heybera zindî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:01, 23 çiriya paşîn 2024 (UTC) == [[misadereker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:51, 24 çiriya paşîn 2024 (UTC) == [[kêmtirkar]] == @[[Bikarhêner:Cyrus the virus|Cyrus the virus]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:11, 24 çiriya paşîn 2024 (UTC) == [[cudagir]] == @[[Bikarhêner:Cyrus the virus|Cyrus the virus]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:15, 24 çiriya paşîn 2024 (UTC) == [[qerwaşgir]] == @[[Bikarhêner:Cyrus the virus|Cyrus the virus]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:42, 24 çiriya paşîn 2024 (UTC) == [[mişkilûr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:45, 24 çiriya paşîn 2024 (UTC) == [[agirbiran]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:54, 24 çiriya paşîn 2024 (UTC) == [[alan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:55, 24 çiriya paşîn 2024 (UTC) == [[alêdû]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 18:56, 24 çiriya paşîn 2024 (UTC) == [[henaseker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 17:37, 25 çiriya paşîn 2024 (UTC) == [[nial]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ji xeynî vê, maneya 2yem ya di madeya "nihal" de (navdêr 2) jî bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:46, 27 çiriya paşîn 2024 (UTC) == [[gilçim]] == @[[Bikarhêner:Roj Rewandiz|Roj Rewandiz]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:28, 29 çiriya paşîn 2024 (UTC) :Ez ji bajarê Kobaniyê me. Min ev peyv ji ser zimanê dê û dapîra xwe girt. Ez gelekî ji vê peyvê bawer im ji ber ku devoka min e. [[Bikarhêner:Roj Rewandiz|Roj Rewandiz]] ([[Gotûbêja bikarhêner:Roj Rewandiz|gotûbêj]]) 19:14, 23 kanûna pêşîn 2024 (UTC) ::@[[Bikarhêner:Roj Rewandiz|Roj Rewandiz]] Mala te ava. Ji ber ku çavkaniyên nivîskî tine ne em ê vê kelîmeyê wek [[Wîkîferheng:Pîvekên_tevlîkirinê#Peyvên_herêmî|herêmî]] nîşan bidin. [[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 12:22, 25 kanûna pêşîn 2024 (UTC) == [[reşeme kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Li gorî çêkera sayfeyê li Kobaniyê li kar e. Loma dikare wekî herêmî nîşankirin —[[Bikarhêner:Balyozxane|Balyozxane]] ([[Gotûbêja bikarhêner:Balyozxane|gotûbêj]]) 14:35, 29 çiriya paşîn 2024 (UTC) :Li Kobaniyê dibêjin: ‘Tizbiyê min li xwe reş û reşeme kir’ Anko Tizbî bir û venegerand Înkar kir. [[Bikarhêner:Roj Rewandiz|Roj Rewandiz]] ([[Gotûbêja bikarhêner:Roj Rewandiz|gotûbêj]]) 23:48, 7 kanûna paşîn 2025 (UTC) = Kanûna pêşîn 2024 = == [[çarkunc]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 00:21, 27 kanûna pêşîn 2024 (UTC) = Kanûna paşîn 2025 = == [[pevnatî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ev kelîme wek kurtkirina ji "bi hev nehatî" hatîye fikirîn, ihtimal e ku wek sunî hatîye çêkirin û bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:43, 6 kanûna paşîn 2025 (UTC) == [[lihevnatîbûn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Wek formeke "lihevnehatîbûn"ê hatîye fikirîn, lê bêçavkanî ye. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:49, 6 kanûna paşîn 2025 (UTC) == [[Binxetê]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Em zanin ku ji ber sînora Tirkîyeyê ku Cizîrê dike du perçe û ji parçeya binî re Binxet tê gotin ku kurd lê dijîn. Feqet min herêma Binxetê seh nekirîye, û bi tedqîqeke neberfireh jî çavkanî peyda nekir ji bo vê peyvê. Lewma bi min mahîyeta vê madeyê û qeydkirina wek "Binxetê" xelet e. Ger ji bo herêma tarîfkirî çavkanî nîn be, em vê madeyê bikin "'''Binxet'''" û tarîfa wê jî bikin "''Navê Rojavayê Kurdistanê ku li binê xeta Tirkîye - Sûrîyeyê dimîne.''" — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 00:22, 24 kanûna paşîn 2025 (UTC) == [[xirecir çêkirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' "''Çê''" bi maneya "''baş''" e, û "''çêkirin''" ji bo tiştên pozîtîv likar e, li nav milet jî ev wisa ye. Lewma ev xelet e. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 20:00, 24 kanûna paşîn 2025 (UTC) == [[sîrkxane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:30, 26 kanûna paşîn 2025 (UTC) == [[anînder]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Di hin çavkanîyan de wek formeke "''der'''''anîn'''"ê hatîye qeydkirin. Lê belê çavkanîyeke eslî (ne ji kitêbên gramerê) peyda nabe ji bo vê kelîmeyê. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 00:24, 27 kanûna paşîn 2025 (UTC) == [[rastî nîşandan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:14, 30 kanûna paşîn 2025 (UTC) = Sibat 2025 = == [[pir hindik#p-mane|pir hindik]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:31, 3 sibat 2025 (UTC) == [[anteroz]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:19, 15 sibat 2025 (UTC) == [[veşidandin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:43, 15 sibat 2025 (UTC) == [[karîwer kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 02:01, 16 sibat 2025 (UTC) == [[hil girtin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:06, 17 sibat 2025 (UTC) == [[wer girtin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:07, 17 sibat 2025 (UTC) == [[avkûre]] == @[[Bikarhêner:Mohajeer|Mohajeer]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:36, 18 sibat 2025 (UTC) == [[derdek girtin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:42, 18 sibat 2025 (UTC) == [[vekitandin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:12, 21 sibat 2025 (UTC) == [[dînperist]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:32, 22 sibat 2025 (UTC) == [[ayînperist]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:33, 22 sibat 2025 (UTC) == [[ayînperistî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:33, 22 sibat 2025 (UTC) == [[ayînperistane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:33, 22 sibat 2025 (UTC) == [[piştrastkirinbar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 01:45, 23 sibat 2025 (UTC) == [[diyarkirinbar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 02:08, 23 sibat 2025 (UTC) == [[hevhatî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 03:52, 1 adar 2025 (UTC) == [[kerexne]] == @[[Bikarhêner:Rojhilat|Rojhilat]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:05, 1 adar 2025 (UTC) == [[pawan#p-mane|pawan]] == Ferhenga Hejar soranî ye, ji bo maneya "cihê qedexekirî, cihê inhisarî" ji xeynî wê çavkanî heye gelo —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:17, 3 adar 2025 (UTC) == [[taqane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 02:11, 6 adar 2025 (UTC) == [[gosirmetî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:07, 6 adar 2025 (UTC) == [[matîjêker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:08, 14 adar 2025 (UTC) == [[hilewesan]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:14, 16 adar 2025 (UTC) == [[peydandin#p-mane|peydandin]] == Ji bo maneya [[dûre]], [[paşî]], [[piştî]] wê / wî ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:26, 19 adar 2025 (UTC) == [[peşar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:36, 19 adar 2025 (UTC) == [[nûzaf]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:30, 20 adar 2025 (UTC) == [[deynname]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 20 adar 2025 (UTC) == [[girtek]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:29, 20 adar 2025 (UTC) == [[bêşxane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:25, 23 adar 2025 (UTC) == [[superleheng]] == @[[Bikarhêner:Avestaboy|Avestaboy]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:34, 23 adar 2025 (UTC) == [[goranparêz]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:54, 27 adar 2025 (UTC) == [[raber#p-mane|raber]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:09, 1 nîsan 2025 (UTC) == [[morîvanî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 20:06, 1 nîsan 2025 (UTC) == [[wevde]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 12:17, 3 nîsan 2025 (UTC) == [[şandêr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:31, 3 nîsan 2025 (UTC) == [[giloverî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Tenê li ser Ferhenga Zaravên Teknîkî û hejmara yekem ya kovara Bernamegehê derbas dibe. —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:10, 4 nîsan 2025 (UTC) == [[xêvjimêr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 00:00, 12 nîsan 2025 (UTC) == [[paşekeft]] == @[[Bikarhêner:Jiyar Hesen|Jiyar Hesen]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:11, 13 nîsan 2025 (UTC) == [[paşekeft kirin]] == @[[Bikarhêner:Jiyar Hesen|Jiyar Hesen]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 11:12, 13 nîsan 2025 (UTC) == [[hawirkanî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:36, 20 nîsan 2025 (UTC) == [[hawirkan]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:18, 21 nîsan 2025 (UTC) : Di çavkanîyeke de "''hawrikan''" (ku wek forma vê kelîmeyê hatîye nîşandayîn û wek "hevrêyan" a kurmancî ye) bi maneya "''friends''"a îngilîzî hatîye emilandin. Wekî din ti çavkanî nîne ji bo vê peyvê. Ger ev "hevrik"a kurmancî be, li vir dîsa xelet e. Bi her awayî divê were jêbirin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:28, 21 nîsan 2025 (UTC) == [[hawirkanî]] == @[[Bikarhêner:Cyrus the virus|Cyrus the virus]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:18, 21 nîsan 2025 (UTC) == [[hawirkanvanî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:18, 21 nîsan 2025 (UTC) == [[riberiz]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:39, 21 nîsan 2025 (UTC) :Ez bawer im ku ev hat peyitandin. Min çavkanî di beşa gotûbêjê de dan û şablona bêçavkanîtiyê rakir. [[Bikarhêner:Ferhengvan|Ferhengvan]] ([[Gotûbêja bikarhêner:Ferhengvan|gotûbêj]]) 06:11, 25 nîsan 2025 (UTC) == [[awam]] == @[[User:Ferhengvan|Ferhengvan]] Min ev kelîme di ferhengên din de nedît. Gelo di kurmancî de tê bikaranîn? —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:09, 23 nîsan 2025 (UTC) :@[[Bikarhêner:Zerwanda|Zerwanda]] tu jî forma [[vam]] ya vê kelîmeyê diemilînî, gelo çavkanî ji bo wê jî heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:11, 23 nîsan 2025 (UTC) ::Min jî li vir peyda kir û dest kar anîn kir, hemin ev li guherrên dengî ên Kurmancî dihat (*ebam > *ewam > *wam > vam; wek *ebaj- > {{q|ku|vajî}}). Min gotibû qey di zarawayê kesekî da hebûye û wî bi ser va kiriye û jixwe nepêkan nîn e ku di zimên da hebe îcar rind e, nê min jî serkanî tine ji bonê. [[Bikarhêner:Zerwanda|Zerwanda]] ([[Gotûbêja bikarhêner:Zerwanda|gotûbêj]]) 21:37, 23 nîsan 2025 (UTC) :::https://bnk.institutkurde.org/images/pdf/4UVB2YMZG3.pdf :::Di vê kitêbê de bi forma wam derbas dibe 1 Wam : qerz, dên. " dîsa jî gereg 3 çavkanî hebe. :::Lê di beşa etîmolojiyê de eger em karibin zimanekî yekgirtî biemilînin, dê baştir be. Wekî din "bi vê enflasyona kelîmeyan" em nikarin ti tiştî bidin fêmkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:54, 23 nîsan 2025 (UTC) == [[arvanê hûr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:48, 27 nîsan 2025 (UTC) == [[damezrak]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:47, 27 nîsan 2025 (UTC) == [[mezrak]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:49, 27 nîsan 2025 (UTC) == [[hevhemetî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 6 gulan 2025 (UTC) == [[navhemetî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 6 gulan 2025 (UTC) == [[entîte]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:08, 17 gulan 2025 (UTC) == [[agirvekujêner]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:35, 21 gulan 2025 (UTC) == [[progres]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 23 gulan 2025 (UTC) == [[bûncî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:29, 28 gulan 2025 (UTC) == [[tasît]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:36, 31 gulan 2025 (UTC) == [[implîcît]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:37, 31 gulan 2025 (UTC) == [[hewedar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 19:14, 31 gulan 2025 (UTC) == [[destajo]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:38, 31 gulan 2025 (UTC) == [[binhimbank]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:23, 1 hezîran 2025 (UTC) == [[şîmane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:31, 2 hezîran 2025 (UTC) == [[materya]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:12, 5 hezîran 2025 (UTC) == [[bêtî]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:03, 7 hezîran 2025 (UTC) == [[rîbat]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:21, 9 hezîran 2025 (UTC) == [[hevtawan]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:26, 12 hezîran 2025 (UTC) :Pirsek: "Hevwate" kelîmeyên ku maneyan wan hev in. "hevtawan" çawa dibe "şirîkên sûcekê"? Gereg sûcên ku tiştê wan hev bin? Ew tişt çi ye? Çima dibe şirîk? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:32, 12 hezîran 2025 (UTC) ::Merheba! ::"Hevtawan" anku "şirîkên heman tawanê, kesên ku heman tawan kiriye/dikin", wek "hevkar = şirîkên heman karî, kesên ku heman karî dikin", "hevjîn = şirîkên heman jiyanê, kesên ku bi hev re dijîn"... [[Bikarhêner:Ferhengvan|Ferhengvan]] ([[Gotûbêja bikarhêner:Ferhengvan|gotûbêj]]) 06:11, 30 hezîran 2025 (UTC) == [[zanîş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 12:07, 14 hezîran 2025 (UTC) == [[zanîşwer]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 12:07, 14 hezîran 2025 (UTC) == [[zanmend]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 12:09, 14 hezîran 2025 (UTC) == [[zandar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:08, 14 hezîran 2025 (UTC) == [[zayîndar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:12, 14 hezîran 2025 (UTC) == [[zayindar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 21:12, 14 hezîran 2025 (UTC) == [[akund]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:16, 16 hezîran 2025 (UTC) == [[destbikar]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 07:41, 22 hezîran 2025 (UTC) == [[vekitandin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:DarkAngel2100|DarkAngel2100]] ([[Gotûbêja bikarhêner:DarkAngel2100|gotûbêj]]) 10:45, 2 tîrmeh 2025 (UTC) Çavkanî: Kurdish-English Dictionary by Baran Rizgar == [[peşarmend]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:54, 5 tîrmeh 2025 (UTC) == [[zaîd]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 06:59, 6 tîrmeh 2025 (UTC) == [[Xrîst]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 07:13, 10 tîrmeh 2025 (UTC) == [[hesibanî kirin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:48, 13 tîrmeh 2025 (UTC) == [[top danîn]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:08, 13 tîrmeh 2025 (UTC) == [[çûkger]] == @[[Bikarhêner:Dûrzan|Dûrzan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:49, 18 tîrmeh 2025 (UTC) == [[dûneçkar]] == @[[Bikarhêner:212.175.166.246|212.175.166.246]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:38, 10 tebax 2025 (UTC) == [[sermirov]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 15:22, 13 tebax 2025 (UTC) == [[nifira Esed]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 16:00, 19 tebax 2025 (UTC) == [[dilkrîz]] == @[[Bikarhêner:Kurd87612|Kurd87612]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:44, 23 tebax 2025 (UTC) == [[berxa Xwedê]] == @[[Bikarhêner:Kurd87612|Kurd87612]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:28, 31 tebax 2025 (UTC) == [[destmatî]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 17:47, 21 îlon 2025 (UTC) == [[sîbaq]] == @[[Bikarhêner:Ferhengvan|Ferhengvan]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 20:33, 27 îlon 2025 (UTC) == [[mirovê daristanê]] == @[[Bikarhêner:Hewarwîkîferheng|Hewarwîkîferheng]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 08:09, 18 çiriya pêşîn 2025 (UTC) == [[çavbijîşk]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:30, 24 çiriya pêşîn 2025 (UTC) == [[makeşar]] == @[[Bikarhêner:Avestaboy|Avestaboy]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 06:46, 14 kanûna pêşîn 2025 (UTC) :Silav @[[Bikarhêner:Bikarhêner|Bikarhêner]] min bi derew peyv çê nekir. Peyva makeşar di sepana O-ferheng û her wiha di ferhenga online ferhengkurdi.org ya '''Enstîtuya Kurdî ya Stenbolê''' tê xwendin. :https://ferhengkurdi.org/tr/kelime/make%C5%9Far-n [[Bikarhêner:Avestaboy|Avestaboy]] ([[Gotûbêja bikarhêner:Avestaboy|gotûbêj]]) 11:07, 18 kanûna pêşîn 2025 (UTC) ::Krîterên me yên tevlîkirina kelîmeyan heye. Ev made rîayeta wan krîteran nake. Lewma min vê kelîmeyê li vir bicih kir. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 23:28, 18 kanûna pêşîn 2025 (UTC) == [[bihîstinzanî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:30, 23 hezîran 2026 (UTC) == [[hevdilî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:28, 24 hezîran 2026 (UTC) == [[kemisandin]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 24 hezîran 2026 (UTC) == [[kexme]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 24 hezîran 2026 (UTC) == [[lêpirsyarker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:32, 24 hezîran 2026 (UTC) == [[raypirs]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:34, 24 hezîran 2026 (UTC) == [[helwestpirs]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:34, 24 hezîran 2026 (UTC) == [[kopirî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' xuyaye maneyeke wekî pir jî heye dikare were zêdekirin —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:33, 25 hezîran 2026 (UTC) == [[merxeme]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:55, 25 hezîran 2026 (UTC) == [[mohl]] == @[[Bikarhêner:Far ati|Far ati]] ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:14, 25 hezîran 2026 (UTC) == [[irfandarî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:30, 26 hezîran 2026 (UTC) == [[axulme]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:20, 30 hezîran 2026 (UTC) == [[şewane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:26, 1 tîrmeh 2026 (UTC) == [[poezî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:27, 1 tîrmeh 2026 (UTC) == [[sebaxker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:20, 1 tîrmeh 2026 (UTC) == [[sebaxfiroş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:21, 1 tîrmeh 2026 (UTC) == [[siboxfiroş]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:21, 1 tîrmeh 2026 (UTC) == [[diyardenasîtî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 1 tîrmeh 2026 (UTC) == [[dawxane]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:41, 3 tîrmeh 2026 (UTC) == [[pêşajotî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:15, 3 tîrmeh 2026 (UTC) == [[kontînentaltirîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:35, 3 tîrmeh 2026 (UTC) == [[azmayîşker]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 3 tîrmeh 2026 (UTC) == [[ateşparêz]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 05:19, 4 tîrmeh 2026 (UTC) == [[kurtetarîx]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:52, 4 tîrmeh 2026 (UTC) == [[besayetî]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:57, 4 tîrmeh 2026 (UTC) == [[tawbijêr]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 4 tîrmeh 2026 (UTC) == [[destbir]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:30, 5 tîrmeh 2026 (UTC) == [[nekider]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:56, 5 tîrmeh 2026 (UTC) == [[niqar]] == —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:39, 5 tîrmeh 2026 (UTC) == [[parêzîn]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' Ev form li ti derê peyda nabe. Berî çendîn salan hatîye çêkirin. Bi qenaeta min şaş e, lewma em jê bibin. — [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 10:29, 6 tîrmeh 2026 (UTC) == [[stêrfizîk]] == . —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:33, 6 tîrmeh 2026 (UTC) == [[pêçavik]] == . Eger ev kelîmeyeke mehelî be, Jkx şablona {{şx|dever|cihê ku lê tê bikaranîn}} li madeyê zêde bike. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:59, 7 tîrmeh 2026 (UTC) == [[qerqef]] == . Eger ev kelîmeyeke mehelî be, Jkx şablona {{şx|dever|cihê ku lê tê bikaranîn}} li madeyê zêde bike. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:17, 7 tîrmeh 2026 (UTC) == [[rakar]] == . Eger ev kelîmeyeke mehelî be, Jkx şablona {{şx|dever|cihê ku lê tê bikaranîn}} li madeyê zêde bike. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:30, 7 tîrmeh 2026 (UTC) == [[remilîn]] == . Eger ev kelîmeyeke mehelî be, Jkx şablona {{şx|dever|cihê ku lê tê bikaranîn}} li madeyê zêde bike. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:36, 7 tîrmeh 2026 (UTC) == [[segurî]] == . —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:51, 7 tîrmeh 2026 (UTC) == [[serdev]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:10, 7 tîrmeh 2026 (UTC) == [[serziman]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:01, 7 tîrmeh 2026 (UTC) == [[tenîsk]] == di Înternetê de gelek netîce derdikevin lê ji bo ku dûre ez kontrol bikim û çavkaniyên sehîh binivîsim li vir îlawe dikim. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 7 tîrmeh 2026 (UTC) == [[tertî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:58, 7 tîrmeh 2026 (UTC) == [[tirut]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:25, 7 tîrmeh 2026 (UTC) == [[tizqa]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:28, 7 tîrmeh 2026 (UTC) == [[têkilîdêr]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:31, 7 tîrmeh 2026 (UTC) == [[tîtalk]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:33, 7 tîrmeh 2026 (UTC) == [[vircoq]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 7 tîrmeh 2026 (UTC) == [[xilêbat]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]]. Li gorî kurteya çêkirina sayfeyê li Qamişlo û Amûdê tê bikaranîn. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 7 tîrmeh 2026 (UTC) == [[xin_û_cûc]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 7 tîrmeh 2026 (UTC) == [[çiteba]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:38, 7 tîrmeh 2026 (UTC) == [[desttêl]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:45, 8 tîrmeh 2026 (UTC) == [[Mîmedol]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:51, 8 tîrmeh 2026 (UTC) == [[alaf]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:01, 8 tîrmeh 2026 (UTC) == [[aşirme]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:13, 10 tîrmeh 2026 (UTC) == [[geledîmentî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:54, 13 tîrmeh 2026 (UTC) == [[çendîmentî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:54, 13 tîrmeh 2026 (UTC) == [[rojeng]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:41, 18 tîrmeh 2026 (UTC) == [[bereyîşk]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:47, 19 tîrmeh 2026 (UTC) == [[wesekname]] == ''[[Wîkîferheng:Pîvekên tevlîkirinê|Çavkanî hewce ye]].'' —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:08, 22 tîrmeh 2026 (UTC) == [[cemaetgeh]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:17, 24 tîrmeh 2026 (UTC) == [[iblîskanî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:27, 30 tîrmeh 2026 (UTC) == [[pehnik]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:51, 4 tebax 2026 (UTC) :# Kamiran bedirxan û H. Muhammed bi maneya guwerte bi kar tînin. :# Navê Çiyayek e li Hawarê derbas dibe :# Farqînî tenê dibêje "yassı" :#* [https://usercontent.one/wp/portal.arsivakurd.org/wp-content/uploads/2021/08/Azadiya-welat_67.pdf?media=1709107483 Azadiya welat hj 67] Roşan Lezgîn kevirên pehnik :# Ferhenga destî pahnik pehnik dibêje :# Rojen barnas li vir bi çi mana bi kar tîne? :#* {{jêder-kitêb|ku|jêgirtin=... '''pehnika''' dirandar tê kişandin û ji nû ve bi mîxên têlkî ve tê girêdan ku firk û sistî nekevê. Piştî ku dirûtina rû bi binpiyê hundirîn ve xelas bû vêca dora binpiyê derva (binî) tê. Binpiyê binî li ser yê hun- dirîn an bitenê bi...|sal=2013|nivîskar=Rojen Barnas, Nûbihar|url=http://books.google.com.tr/books?id=pVDKEAAAQBAJ&pg=PA67&dq=%22wan+pehnikan%22+OR+%22pehnik%C3%AA%22+OR+%22pehnikin%22+OR+%22pehnikek%C3%AA%22+OR+%22pehnika%22+OR+%22w%C3%AA+pehnik%C3%AA%22+OR+%22pehnikino%22+OR+%22pehnikine%22&hl=&cd=4&source=gbs_api|sernav=Sîlvan/ Silîvan / Farqîn|weşanxane=PAK AJANS YAYINCILIK TURİZM VE DIŞ TİCARET LİMİTED ŞİRKETİ|ISBN=9786055402884|rûpel=67}} :[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:40, 6 tebax 2026 (UTC) == [[temirîndox]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:56, 6 tebax 2026 (UTC) == [[kansêrolojî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:17, 6 tebax 2026 (UTC) == [[navçeparêzî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:09, 6 tebax 2026 (UTC) == [[goganîbûnî]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:23, 15 tebax 2026 (UTC) == [[zewijandin]] == Bêyî çavkaniyan e. --[[Bikarhêner:Şêr|Şêr]] ([[Gotûbêja bikarhêner:Şêr|gotûbêj]]) 18:34, 22 tebax 2026 (UTC) == [[karbes]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 12:42, 29 tebax 2026 (UTC) == [[biehliyet]] == Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:41, 31 tebax 2026 (UTC) l73xne3qifpblfoojp2c2dag2b6oxcd सैकंड 0 1092098 6793951 6793945 2026-10-14T17:16:05Z Wikihez 7344 /* Hindî */ 6793951 wikitext text/x-wiki == {{ziman|hi}} == === Navdêr === {{navdêr|hi|z=n}} # {{mijar|hi|dem}} {{g|ku|sanî}}, {{g|ku|saniye}}, {{g|ku|çirke}} #: {{hevmane|hi|सानिया}} 2id4ik3y96gagaxx258b8ii2b9rcvkz Modul:parameters/data 828 1324875 6794019 6534232 2026-10-14T19:02:04Z Wikihez 7344 trans-top: sense number (2) and stûn, as {{werger-ser}} has them 6794019 Scribunto text/plain local list_to_set = require("Module:table").listToSet local function process_set_param(param) param.set = list_to_set(param.set) param.converted_set = true return param end local boolean = {type = "boolean"} local empty_list = {} local list = {list = true} local list_allow_holes_separate_no_index = {list = true, allow_holes = true, separate_no_index = true} local required = {required = true} local required_default_ = {required = true, default = ""} local required_lang_default_und = {required = true, type = "language", default = "und"} local m = {} -- [[Module:anchors]] m["anchor"] = { [1] = {required = true, list = true, disallow_holes = true}, } m["senseid"] = { [1] = required_lang_default_und, [2] = required_default_, tag = process_set_param{set = {"li", "p"}, default = "li"}, } m["etymid"] = { [1] = required_lang_default_und, [2] = required_default_ } -- [[Module:etymon]] m["etymon"] = { [1] = required_lang_default_und, [2] = {list = true, disallow_holes = true}, id = true, title = true, tree = boolean, text = true, -- Kurdish names for the same two parameters. dar = {alias_of = "tree"}, metn = {alias_of = "text"}, exnihilo = boolean, etydate = true, doublet = {sublist = "comma without whitespace"}, -- Kurdish name for the same parameter, cf. Şablon:cotreh. cotreh = {alias_of = "doublet"}, rfe = true, etystub = true, nl = boolean, pos = true, -- Kurdish name for the same parameter; Modul:etymon maps its Kurdish values (reh, kelîme, ...). crz = {alias_of = "pos"}, notree = {default = false, type = "boolean"}, nocat = {default = false, type = "boolean"}, nodot = {default = false, type = "boolean"}, dot = true, -- Kurdish names for the same parameters. ["tarîxa-etîm"] = {alias_of = "etydate"}, darna = {alias_of = "notree"}, katna = {alias_of = "nocat"}, nekat = {alias_of = "nocat"}, niqtena = {alias_of = "nodot"}, niqte = {alias_of = "dot"}, json = {default = false, type = "boolean"}, } -- [[Module:translations]] m["translation"] = { [1] = required_lang_default_und, [2] = true, [3] = list, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } m["t-needed"] = { [1] = required_lang_default_und, [2] = process_set_param{set = {"usex", "quote"}}, ["nocat"] = boolean, ["katna"] = {alias_of = "nocat"}, ["nekat"] = {alias_of = "nocat"}, ["sort"] = true, } m["trans-top"] = { [1] = true, -- Sense number shown after the gloss, as {{werger-ser}} has it. [2] = true, ["id"] = true, ["column-width"] = true, -- Fixed number of columns. ["stûn"] = true, } m["trans-top-also"] = { [1] = required, [2] = list, ["id"] = list_allow_holes_separate_no_index, ["column-width"] = true, } m["checktrans-top"] = { [1] = true, ["column-width"] = true, } m["trans-bottom"] = empty_list m["trans-see"] = { [1] = required, [2] = list, ["id"] = list_allow_holes_separate_no_index, } m["translation subpage"] = empty_list m["no equivalent translation"] = { [1] = required_lang_default_und, ["noend"] = boolean, } m["no attested translation"] = { [1] = required_lang_default_und, ["noend"] = boolean, ["sort"] = true, } m["not used"] = { [1] = required_lang_default_und, [2] = true, } return m gd2b82lakx3cy1uek0e2sjdow6pcbza 6794079 6794019 2026-10-14T21:24:39Z Wikihez 7344 translation: Kurdish aliases {{W}} has used (cuda, dif, R, guman, xs) 6794079 Scribunto text/plain local list_to_set = require("Module:table").listToSet local function process_set_param(param) param.set = list_to_set(param.set) param.converted_set = true return param end local boolean = {type = "boolean"} local empty_list = {} local list = {list = true} local list_allow_holes_separate_no_index = {list = true, allow_holes = true, separate_no_index = true} local required = {required = true} local required_default_ = {required = true, default = ""} local required_lang_default_und = {required = true, type = "language", default = "und"} local m = {} -- [[Module:anchors]] m["anchor"] = { [1] = {required = true, list = true, disallow_holes = true}, } m["senseid"] = { [1] = required_lang_default_und, [2] = required_default_, tag = process_set_param{set = {"li", "p"}, default = "li"}, } m["etymid"] = { [1] = required_lang_default_und, [2] = required_default_ } -- [[Module:etymon]] m["etymon"] = { [1] = required_lang_default_und, [2] = {list = true, disallow_holes = true}, id = true, title = true, tree = boolean, text = true, -- Kurdish names for the same two parameters. dar = {alias_of = "tree"}, metn = {alias_of = "text"}, exnihilo = boolean, etydate = true, doublet = {sublist = "comma without whitespace"}, -- Kurdish name for the same parameter, cf. Şablon:cotreh. cotreh = {alias_of = "doublet"}, rfe = true, etystub = true, nl = boolean, pos = true, -- Kurdish name for the same parameter; Modul:etymon maps its Kurdish values (reh, kelîme, ...). crz = {alias_of = "pos"}, notree = {default = false, type = "boolean"}, nocat = {default = false, type = "boolean"}, nodot = {default = false, type = "boolean"}, dot = true, -- Kurdish names for the same parameters. ["tarîxa-etîm"] = {alias_of = "etydate"}, darna = {alias_of = "notree"}, katna = {alias_of = "nocat"}, nekat = {alias_of = "nocat"}, niqtena = {alias_of = "nodot"}, niqte = {alias_of = "dot"}, json = {default = false, type = "boolean"}, } -- [[Module:translations]] m["translation"] = { [1] = required_lang_default_und, [2] = true, [3] = list, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, -- Kurdish names {{W}} has used for the same parameters. ["cuda"] = {alias_of = "alt"}, ["dif"] = {alias_of = "alt"}, ["R"] = {alias_of = "tr"}, -- guman=erê marks the translation for checking, as {{t-check}} does. ["guman"] = true, -- Accepted and not used. ["xs"] = true, } m["t-needed"] = { [1] = required_lang_default_und, [2] = process_set_param{set = {"usex", "quote"}}, ["nocat"] = boolean, ["katna"] = {alias_of = "nocat"}, ["nekat"] = {alias_of = "nocat"}, ["sort"] = true, } m["trans-top"] = { [1] = true, -- Sense number shown after the gloss, as {{werger-ser}} has it. [2] = true, ["id"] = true, ["column-width"] = true, -- Fixed number of columns. ["stûn"] = true, } m["trans-top-also"] = { [1] = required, [2] = list, ["id"] = list_allow_holes_separate_no_index, ["column-width"] = true, } m["checktrans-top"] = { [1] = true, ["column-width"] = true, } m["trans-bottom"] = empty_list m["trans-see"] = { [1] = required, [2] = list, ["id"] = list_allow_holes_separate_no_index, } m["translation subpage"] = empty_list m["no equivalent translation"] = { [1] = required_lang_default_und, ["noend"] = boolean, } m["no attested translation"] = { [1] = required_lang_default_und, ["noend"] = boolean, ["sort"] = true, } m["not used"] = { [1] = required_lang_default_und, [2] = true, } return m dtntbx8r8btbxwhcfppl5g4ce9gu7p0 Şablon:werger-ser/ceribandin 10 1325436 6793959 6099817 2026-10-14T18:12:09Z Wikihez 7344 Test copy: box from Modul:werger/ceribandin, read by the translation adder gadget 6793959 wikitext text/x-wiki <templatestyles src="Şablon:werger-ser/style.css"/>{{#invoke:werger/ceribandin|top}}<noinclude> </td></tr></table></div></div> Test copy of {{ş|werger-ser}} that opens a box [[MediaWiki:Gadget-wikt.translation-adder.js]] can add translations to. </noinclude> 08qc5x7gcrukb4r7iee1fkkfyrkepyj 6794021 6793959 2026-10-14T19:02:16Z Wikihez 7344 Test copy using Modul:translations/ceribandin 6794021 wikitext text/x-wiki {{#invoke:translations/ceribandin|top}}<noinclude> </td></tr></table></div></div> Test copy of {{ş|werger-ser}} using [[Modul:translations/ceribandin]], en.wiktionary's {{en:trans-top}}. </noinclude> 8n1m9ahqqhjv4bfb012lk9zb8e84xlb Şablon:werger-bin/ceribandin 10 1325437 6793960 6099818 2026-10-14T18:12:14Z Wikihez 7344 Test copy: box from Modul:werger/ceribandin, read by the translation adder gadget 6793960 wikitext text/x-wiki {{#invoke:werger/ceribandin|bottom}}<noinclude> Test copy of {{ş|werger-bin}}. </noinclude> 1e50eol5zas5jppyjellm6pre423gef 6794022 6793960 2026-10-14T19:02:21Z Wikihez 7344 Test copy using Modul:translations/ceribandin 6794022 wikitext text/x-wiki {{#invoke:translations/ceribandin|bottom}}<noinclude> Test copy of {{ş|werger-bin}} (en.wiktionary's trans-bottom). </noinclude> 9ho1lz63oiik4uuaw64as17jn871zju Şablon:W+/ceribandin 10 1325566 6794025 6100565 2026-10-14T19:02:36Z Wikihez 7344 Test copy using Modul:translations/ceribandin 6794025 wikitext text/x-wiki {{#invoke:translations/ceribandin|show|interwiki=tpos}}<noinclude> Test copy of {{ş|W+}} (en.wiktionary's t+). </noinclude> b9ccv7o0u36eono2z5gkragq5o2k87k 6794050 6794025 2026-10-14T19:41:33Z Wikihez 7344 TemplateData for the translation template, read by the template dialog 6794050 wikitext text/x-wiki {{#invoke:translations/ceribandin|show|interwiki=tpos}}<noinclude> Test copy of {{ş|W+}} (en.wiktionary's t+). <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string" }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string" }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id" ] } </templatedata> </noinclude> mtecu6crohdey0n81ooe6wgnnf3cbfn 6794082 6794050 2026-10-14T21:24:56Z Wikihez 7344 TemplateData: aliases cuda, dif, R and the guman parameter 6794082 wikitext text/x-wiki {{#invoke:translations/ceribandin|show|interwiki=tpos}}<noinclude> Test copy of {{ş|W+}} (en.wiktionary's t+). <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string", "aliases": [ "cuda", "dif" ] }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string", "aliases": [ "R" ] }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" }, "guman": { "label": "Guman", "description": "erê: tercime hê ne kontrolkirî ye; li ber wê \"ji kerema xwe kontrol bike\" tê nivîsîn û rûpel dikeve Kategorî:Kontrola wergerê.", "type": "string", "suggestedvalues": [ "erê" ] } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id", "guman" ] } </templatedata> </noinclude> jwnt27mqcyy7alklt1wyjm6d1wzdcq3 Şablon:W-/ceribandin 10 1325633 6794026 6101875 2026-10-14T19:02:41Z Wikihez 7344 Test copy using Modul:translations/ceribandin 6794026 wikitext text/x-wiki {{#invoke:translations/ceribandin|show}}<noinclude> Test copy of {{ş|W-}} (en.wiktionary's t). </noinclude> 1ugu6k4e47k4n7o9a0vbynjbvarl9zi 6794051 6794026 2026-10-14T19:41:38Z Wikihez 7344 TemplateData for the translation template, read by the template dialog 6794051 wikitext text/x-wiki {{#invoke:translations/ceribandin|show}}<noinclude> Test copy of {{ş|W-}} (en.wiktionary's t). <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string" }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string" }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id" ] } </templatedata> </noinclude> 0zexzxjjjcxrsytho5m95yqpe4tre61 6794083 6794051 2026-10-14T21:25:01Z Wikihez 7344 TemplateData: aliases cuda, dif, R and the guman parameter 6794083 wikitext text/x-wiki {{#invoke:translations/ceribandin|show}}<noinclude> Test copy of {{ş|W-}} (en.wiktionary's t). <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string", "aliases": [ "cuda", "dif" ] }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string", "aliases": [ "R" ] }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" }, "guman": { "label": "Guman", "description": "erê: tercime hê ne kontrolkirî ye; li ber wê \"ji kerema xwe kontrol bike\" tê nivîsîn û rûpel dikeve Kategorî:Kontrola wergerê.", "type": "string", "suggestedvalues": [ "erê" ] } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id", "guman" ] } </templatedata> </noinclude> 8mo8hq2m17jqylnlfnuwk70owxmla8j li dû hevalan man 0 1327023 6793949 6793925 2026-10-14T17:13:32Z Wikihez 7344 Kurmancî/Biwêj: beşa «Etîmolojî» hate rakirin; Kurmancî: beşa «Biwêj» bû «Lêker» (DefSideBoxes) 6793949 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Lêker === {{lêker|ku|c=h-ngh}} # {{mane?|ku}} ==== Werger ==== {{werger-ser}} * {{Z|en}}: {{W+|en|left behind}}, {{W+|en|fall behind}} {{werger-bin}} gt79i3407e3dl9gm6tcnssz3w176w83 Bikarhêner:Wikihez/Gadget-test/loader.js 2 1358398 6793969 6730902 2026-10-14T18:20:37Z Wikihez 7344 Load the translation adder test copy 6793969 javascript text/javascript // <nowiki> // Test harness for the gadgets. Loaded from [[MediaWiki:Gadget-test.js]], which is one line: // mw.loader.load('//ku.wiktionary.org/w/index.php?title=Bikarhêner:Wikihez/Gadget-test/loader.js&action=raw&ctype=text/javascript'); // // Runs the userspace copies in place of the site's gadgets, under the same module names. A gadget the // wiki has registered but not yet loaded is implemented here from the test page instead, so its // dependencies are the real ones from [[MediaWiki:Gadgets-definition]]. One already loading from the wiki // cannot be replaced: turn the default gadgets that load it (start and wikt.dsb load wikt.core) off in // Special:Preferences while testing, and this names any that are still on. // // MODULES lists only the gadgets with changes not yet live; everything else runs the site's copy. A // gadget is added back when it is being changed, and taken out once that change is deployed. (function () { "use strict"; // Set synchronously, before add-examples' DOM-ready pass, so its own inline link is never built. window.wikt = window.wikt || {}; wikt.addExamples = wikt.addExamples || {}; wikt.addExamples.suppressLinks = true; // Subpage segments are case-sensitive, so BASE must match the pages exactly. var BASE = "Bikarhêner:Wikihez/Gadget-test"; // A value carrying a namespace is used as the whole title; anything else is appended to BASE. function pageTitle(suffix) { return suffix.indexOf(":") !== -1 ? suffix : BASE + suffix; } // module name -> package ({main, files}) or plain script ({script}), plus styles. // deferred held back until something asks for it, as production fetches it only then // start asked for on page load, as production loads a default gadget // deps only for a gadget not yet in Gadgets-definition; a registered one keeps the wiki's // actions with start, only on these wgAction values, as a definition's "actions=" does var MODULES = { "ext.gadget.wikt.edit": { main: "wikt.edit.js", files: { "wikt.edit.js": "/wikt-edit.js", "wikt.edit/util.js": "/util.js", "wikt.edit/wikitext.js": "/wikitext.js", "wikt.edit/langs.js": "/langs.js", "wikt.edit/labels.js": "/labels.js", "wikt.edit/posparams.js": "/posparams.js", "wikt.edit/data-main.json": "/data-main.json", "wikt.edit/ziman-core.json": "/ziman-core.json" }, styles: ["/wikt-edit.css"] }, // Its own page, not a Gadget-test subpage: a value carrying a namespace is used as the title. "ext.gadget.wikt.add-examples": { deferred: true, main: "wikt.add-examples.js", files: { "wikt.add-examples.js": "Bikarhêner:Wikihez/Gadget-wikt.add-examples.js" }, styles: ["Bikarhêner:Wikihez/Gadget-wikt.add-examples.css"] }, // Turn the site's "wikt.dsb" off in Preferences while this is armed, or the wiki's copy wins. "ext.gadget.wikt.dsb": { start: true, main: "wikt.dsb.js", files: { "wikt.dsb.js": "/dsb.js" }, styles: ["/dsb.css"] }, // The launcher (the site's "start" gadget) asks for the wizard on the first click. "ext.gadget.wikt.start-wizard": { deferred: true, main: "wikt.start-wizard.js", files: { "wikt.start-wizard.js": ".js" }, styles: [".css"] }, // Where the sense panels live: dsb.js asks for it on the first pencil click. "ext.gadget.wikt.dsb-panels": { deferred: true, main: "wikt.dsb-panels.js", files: { "wikt.dsb-panels.js": "/dsb-panels.js" }, styles: ["/dsb-panels.css"] }, // Not in Gadgets-definition yet, so deps are given here. Turn "translation_editor" off while testing. "ext.gadget.wikt.translation-adder": { start: true, actions: ["view"], deps: ["mediawiki.util", "mediawiki.api", "mediawiki.storage", "ext.gadget.StorageUtils"], main: "wikt.translation-adder.js", files: { "wikt.translation-adder.js": "/translation-adder.js", "wikt.translation-adder/editor.js": "/translation-adder/editor.js", "wikt.translation-adder/language-utils.js": "/translation-adder/language-utils.js", "wikt.translation-adder/data.js": "/translation-adder/data.js" }, styles: [] }, "ext.gadget.wikt.cite": { main: "wikt.cite.js", files: { "wikt.cite.js": "/cite.js", "wikt.cite/etimon.js": "/etimon.js" }, styles: ["/cite.css"] } }; // Force dataType "text": otherwise jQuery sees action=raw's JavaScript Content-Type and runs it. // maxage/smaxage=0 and cache:false because the browser otherwise serves a cached copy of the page, // and a test run against yesterday's file looks like a bug in today's. function raw(suffix) { return $.get({ url: mw.util.getUrl(pageTitle(suffix), { action: "raw", maxage: 0, smaxage: 0 }), dataType: "text", cache: false }); } function run() { var names = Object.keys(MODULES); // A module the wiki has started loading cannot be implemented a second time. var busy = names.filter(function (name) { var state = mw.loader.getState(name); return state !== null && state !== "registered"; }); if (busy.length) { console.warn("loader.js: already loaded from the wiki, so the site's copy runs: " + busy.join(", ")); mw.notify("loader.js: van amûran di Tercîhan de bigire, da ku kopiya ceribandinê bixebite: " + busy.map(function (n) { return n.replace("ext.gadget.", ""); }).join(", "), { type: "warn" }); } var ours = names.filter(function (name) { return busy.indexOf(name) === -1; }); // Every distinct page this harness needs, fetched once each. var suffixes = []; function need(suffix) { if (suffixes.indexOf(suffix) === -1) { suffixes.push(suffix); } } ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { need(m.script); } else { Object.keys(m.files).forEach(function (key) { need(m.files[key]); }); } m.styles.forEach(need); }); if (!suffixes.length) { return; } $.when.apply($, suffixes.map(raw)).done(function () { var args = suffixes.length === 1 ? [arguments] : [].slice.call(arguments); var text = {}; suffixes.forEach(function (s, i) { text[s] = args[i][0]; }); // Styles go in directly: a stylesheet passed to implement() that never fires its load event // leaves the module stuck in "executing", and everything queued behind it with it. function addStyles(styles) { (styles || []).forEach(function (suffix) { if (text[suffix]) { mw.util.addCSS(text[suffix]); } }); } // Kept from before the patch below, so requestSiteDeps() does not re-enter it. var origUsing = mw.loader.using; var bad = null; var payloads = {}; ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { payloads[name] = new Function("$", "jQuery", "require", "module", text[m.script]); return; } var files = {}; Object.keys(m.files).forEach(function (key) { var src = text[m.files[key]]; if (key.slice(-5) === ".json") { try { files[key] = JSON.parse(src); } catch (e) { bad = m.files[key] + " nederbasdar e (JSON parse ket): " + e.message; } return; } files[key] = new Function("require", "module", "exports", src); }); payloads[name] = { main: m.main, files: files }; }); if (bad) { mw.notify(bad, { type: "error" }); return; } // implement() runs a module as soon as its dependencies are ready, so a deferred one is // handed over only when something asks for it. function implement(name) { if (!payloads[name]) { return; } var payload = payloads[name]; delete payloads[name]; if (mw.loader.getState(name) === null) { mw.loader.register(name, "0", MODULES[name].deps || []); } else if (mw.loader.getState(name) !== "registered") { console.warn("loader.js: " + name + " is already loading from the wiki; its copy runs."); return; } mw.loader.implement(name, payload); addStyles(MODULES[name].styles); requestSiteDeps(name); } // ResourceLoader fetches a module only when something asks for it, and implement() is not // asking: a gadget handed over here waits at "loaded" for good on a dependency the harness // does not carry and nobody requested -- ext.gadget.citoid-helper behind wikt.cite, say, and // wikt.dsb-panels behind that. Its own copies are left out; those are implemented, not fetched. function requestSiteDeps(name) { var entry = mw.loader.moduleRegistry[name]; var deps = ((entry && entry.dependencies) || []).filter(function (dep) { return !MODULES[dep]; }); if (deps.length) { origUsing.call(mw.loader, deps); } } // A deferred module reached as another's dependency is handed over too, or ResourceLoader // fetches the site's copy of it instead. function withDeps(list) { var out = []; (function walk(items) { (items || []).forEach(function (n) { if (out.indexOf(n) !== -1) { return; } out.push(n); var entry = mw.loader.moduleRegistry[n]; walk(entry ? entry.dependencies : MODULES[n] && MODULES[n].deps); }); }(list)); return out; } mw.loader.using = function (modules) { withDeps([].concat(modules || [])).forEach(implement); return origUsing.apply(this, arguments); }; ours.forEach(function (name) { if (!MODULES[name].deferred) { implement(name); } }); var action = mw.config.get("wgAction"); var starting = ours.filter(function (name) { var m = MODULES[name]; return m.start && (!m.actions || m.actions.indexOf(action) !== -1); }); var settled = false; mw.loader.using(starting).then(function () { settled = true; console.log("loader.js: started " + starting.join(", ")); }, function (err) { settled = true; mw.notify("Xeta barkirinê: " + err, { type: "error" }); }); // A dependency stuck half-loaded leaves using() pending forever, with no error at all. setTimeout(function () { if (settled) { return; } var stuck = withDeps(starting) .filter(function (m) { return mw.loader.getState(m) !== "ready"; }) .map(function (m) { return m + "=" + mw.loader.getState(m); }); mw.notify("Barkirin asê ma: " + stuck.join(", "), { type: "warn" }); }, 10000); }).fail(function () { mw.notify("Nikare hemû rûpelên " + BASE + " bixwîne: " + suffixes.join(", "), { type: "error" }); }); } // The test gadget declares no dependencies, so mw.util is not there yet when this runs. mw.loader.using("mediawiki.util").then(run); })(); // </nowiki> lfwsl4n2m1ubbrw4mcdlbtb9c66utej 6794004 6793969 2026-10-14T18:30:48Z Wikihez 7344 Spacing for the translation adder form 6794004 javascript text/javascript // <nowiki> // Test harness for the gadgets. Loaded from [[MediaWiki:Gadget-test.js]], which is one line: // mw.loader.load('//ku.wiktionary.org/w/index.php?title=Bikarhêner:Wikihez/Gadget-test/loader.js&action=raw&ctype=text/javascript'); // // Runs the userspace copies in place of the site's gadgets, under the same module names. A gadget the // wiki has registered but not yet loaded is implemented here from the test page instead, so its // dependencies are the real ones from [[MediaWiki:Gadgets-definition]]. One already loading from the wiki // cannot be replaced: turn the default gadgets that load it (start and wikt.dsb load wikt.core) off in // Special:Preferences while testing, and this names any that are still on. // // MODULES lists only the gadgets with changes not yet live; everything else runs the site's copy. A // gadget is added back when it is being changed, and taken out once that change is deployed. (function () { "use strict"; // Set synchronously, before add-examples' DOM-ready pass, so its own inline link is never built. window.wikt = window.wikt || {}; wikt.addExamples = wikt.addExamples || {}; wikt.addExamples.suppressLinks = true; // Subpage segments are case-sensitive, so BASE must match the pages exactly. var BASE = "Bikarhêner:Wikihez/Gadget-test"; // A value carrying a namespace is used as the whole title; anything else is appended to BASE. function pageTitle(suffix) { return suffix.indexOf(":") !== -1 ? suffix : BASE + suffix; } // module name -> package ({main, files}) or plain script ({script}), plus styles. // deferred held back until something asks for it, as production fetches it only then // start asked for on page load, as production loads a default gadget // deps only for a gadget not yet in Gadgets-definition; a registered one keeps the wiki's // actions with start, only on these wgAction values, as a definition's "actions=" does var MODULES = { "ext.gadget.wikt.edit": { main: "wikt.edit.js", files: { "wikt.edit.js": "/wikt-edit.js", "wikt.edit/util.js": "/util.js", "wikt.edit/wikitext.js": "/wikitext.js", "wikt.edit/langs.js": "/langs.js", "wikt.edit/labels.js": "/labels.js", "wikt.edit/posparams.js": "/posparams.js", "wikt.edit/data-main.json": "/data-main.json", "wikt.edit/ziman-core.json": "/ziman-core.json" }, styles: ["/wikt-edit.css"] }, // Its own page, not a Gadget-test subpage: a value carrying a namespace is used as the title. "ext.gadget.wikt.add-examples": { deferred: true, main: "wikt.add-examples.js", files: { "wikt.add-examples.js": "Bikarhêner:Wikihez/Gadget-wikt.add-examples.js" }, styles: ["Bikarhêner:Wikihez/Gadget-wikt.add-examples.css"] }, // Turn the site's "wikt.dsb" off in Preferences while this is armed, or the wiki's copy wins. "ext.gadget.wikt.dsb": { start: true, main: "wikt.dsb.js", files: { "wikt.dsb.js": "/dsb.js" }, styles: ["/dsb.css"] }, // The launcher (the site's "start" gadget) asks for the wizard on the first click. "ext.gadget.wikt.start-wizard": { deferred: true, main: "wikt.start-wizard.js", files: { "wikt.start-wizard.js": ".js" }, styles: [".css"] }, // Where the sense panels live: dsb.js asks for it on the first pencil click. "ext.gadget.wikt.dsb-panels": { deferred: true, main: "wikt.dsb-panels.js", files: { "wikt.dsb-panels.js": "/dsb-panels.js" }, styles: ["/dsb-panels.css"] }, // Not in Gadgets-definition yet, so deps are given here. Turn "translation_editor" off while testing. "ext.gadget.wikt.translation-adder": { start: true, actions: ["view"], deps: ["mediawiki.util", "mediawiki.api", "mediawiki.storage", "ext.gadget.StorageUtils"], main: "wikt.translation-adder.js", files: { "wikt.translation-adder.js": "/translation-adder.js", "wikt.translation-adder/editor.js": "/translation-adder/editor.js", "wikt.translation-adder/language-utils.js": "/translation-adder/language-utils.js", "wikt.translation-adder/data.js": "/translation-adder/data.js" }, styles: ["/translation-adder.css"] }, "ext.gadget.wikt.cite": { main: "wikt.cite.js", files: { "wikt.cite.js": "/cite.js", "wikt.cite/etimon.js": "/etimon.js" }, styles: ["/cite.css"] } }; // Force dataType "text": otherwise jQuery sees action=raw's JavaScript Content-Type and runs it. // maxage/smaxage=0 and cache:false because the browser otherwise serves a cached copy of the page, // and a test run against yesterday's file looks like a bug in today's. function raw(suffix) { return $.get({ url: mw.util.getUrl(pageTitle(suffix), { action: "raw", maxage: 0, smaxage: 0 }), dataType: "text", cache: false }); } function run() { var names = Object.keys(MODULES); // A module the wiki has started loading cannot be implemented a second time. var busy = names.filter(function (name) { var state = mw.loader.getState(name); return state !== null && state !== "registered"; }); if (busy.length) { console.warn("loader.js: already loaded from the wiki, so the site's copy runs: " + busy.join(", ")); mw.notify("loader.js: van amûran di Tercîhan de bigire, da ku kopiya ceribandinê bixebite: " + busy.map(function (n) { return n.replace("ext.gadget.", ""); }).join(", "), { type: "warn" }); } var ours = names.filter(function (name) { return busy.indexOf(name) === -1; }); // Every distinct page this harness needs, fetched once each. var suffixes = []; function need(suffix) { if (suffixes.indexOf(suffix) === -1) { suffixes.push(suffix); } } ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { need(m.script); } else { Object.keys(m.files).forEach(function (key) { need(m.files[key]); }); } m.styles.forEach(need); }); if (!suffixes.length) { return; } $.when.apply($, suffixes.map(raw)).done(function () { var args = suffixes.length === 1 ? [arguments] : [].slice.call(arguments); var text = {}; suffixes.forEach(function (s, i) { text[s] = args[i][0]; }); // Styles go in directly: a stylesheet passed to implement() that never fires its load event // leaves the module stuck in "executing", and everything queued behind it with it. function addStyles(styles) { (styles || []).forEach(function (suffix) { if (text[suffix]) { mw.util.addCSS(text[suffix]); } }); } // Kept from before the patch below, so requestSiteDeps() does not re-enter it. var origUsing = mw.loader.using; var bad = null; var payloads = {}; ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { payloads[name] = new Function("$", "jQuery", "require", "module", text[m.script]); return; } var files = {}; Object.keys(m.files).forEach(function (key) { var src = text[m.files[key]]; if (key.slice(-5) === ".json") { try { files[key] = JSON.parse(src); } catch (e) { bad = m.files[key] + " nederbasdar e (JSON parse ket): " + e.message; } return; } files[key] = new Function("require", "module", "exports", src); }); payloads[name] = { main: m.main, files: files }; }); if (bad) { mw.notify(bad, { type: "error" }); return; } // implement() runs a module as soon as its dependencies are ready, so a deferred one is // handed over only when something asks for it. function implement(name) { if (!payloads[name]) { return; } var payload = payloads[name]; delete payloads[name]; if (mw.loader.getState(name) === null) { mw.loader.register(name, "0", MODULES[name].deps || []); } else if (mw.loader.getState(name) !== "registered") { console.warn("loader.js: " + name + " is already loading from the wiki; its copy runs."); return; } mw.loader.implement(name, payload); addStyles(MODULES[name].styles); requestSiteDeps(name); } // ResourceLoader fetches a module only when something asks for it, and implement() is not // asking: a gadget handed over here waits at "loaded" for good on a dependency the harness // does not carry and nobody requested -- ext.gadget.citoid-helper behind wikt.cite, say, and // wikt.dsb-panels behind that. Its own copies are left out; those are implemented, not fetched. function requestSiteDeps(name) { var entry = mw.loader.moduleRegistry[name]; var deps = ((entry && entry.dependencies) || []).filter(function (dep) { return !MODULES[dep]; }); if (deps.length) { origUsing.call(mw.loader, deps); } } // A deferred module reached as another's dependency is handed over too, or ResourceLoader // fetches the site's copy of it instead. function withDeps(list) { var out = []; (function walk(items) { (items || []).forEach(function (n) { if (out.indexOf(n) !== -1) { return; } out.push(n); var entry = mw.loader.moduleRegistry[n]; walk(entry ? entry.dependencies : MODULES[n] && MODULES[n].deps); }); }(list)); return out; } mw.loader.using = function (modules) { withDeps([].concat(modules || [])).forEach(implement); return origUsing.apply(this, arguments); }; ours.forEach(function (name) { if (!MODULES[name].deferred) { implement(name); } }); var action = mw.config.get("wgAction"); var starting = ours.filter(function (name) { var m = MODULES[name]; return m.start && (!m.actions || m.actions.indexOf(action) !== -1); }); var settled = false; mw.loader.using(starting).then(function () { settled = true; console.log("loader.js: started " + starting.join(", ")); }, function (err) { settled = true; mw.notify("Xeta barkirinê: " + err, { type: "error" }); }); // A dependency stuck half-loaded leaves using() pending forever, with no error at all. setTimeout(function () { if (settled) { return; } var stuck = withDeps(starting) .filter(function (m) { return mw.loader.getState(m) !== "ready"; }) .map(function (m) { return m + "=" + mw.loader.getState(m); }); mw.notify("Barkirin asê ma: " + stuck.join(", "), { type: "warn" }); }, 10000); }).fail(function () { mw.notify("Nikare hemû rûpelên " + BASE + " bixwîne: " + suffixes.join(", "), { type: "error" }); }); } // The test gadget declares no dependencies, so mw.util is not there yet when this runs. mw.loader.using("mediawiki.util").then(run); })(); // </nowiki> p5dj6o2uu5k3z6gkg7laru77iww9fpb 6794005 6794004 2026-10-14T18:31:18Z Wikihez 7344 Comment: translation_editor does not touch the new boxes 6794005 javascript text/javascript // <nowiki> // Test harness for the gadgets. Loaded from [[MediaWiki:Gadget-test.js]], which is one line: // mw.loader.load('//ku.wiktionary.org/w/index.php?title=Bikarhêner:Wikihez/Gadget-test/loader.js&action=raw&ctype=text/javascript'); // // Runs the userspace copies in place of the site's gadgets, under the same module names. A gadget the // wiki has registered but not yet loaded is implemented here from the test page instead, so its // dependencies are the real ones from [[MediaWiki:Gadgets-definition]]. One already loading from the wiki // cannot be replaced: turn the default gadgets that load it (start and wikt.dsb load wikt.core) off in // Special:Preferences while testing, and this names any that are still on. // // MODULES lists only the gadgets with changes not yet live; everything else runs the site's copy. A // gadget is added back when it is being changed, and taken out once that change is deployed. (function () { "use strict"; // Set synchronously, before add-examples' DOM-ready pass, so its own inline link is never built. window.wikt = window.wikt || {}; wikt.addExamples = wikt.addExamples || {}; wikt.addExamples.suppressLinks = true; // Subpage segments are case-sensitive, so BASE must match the pages exactly. var BASE = "Bikarhêner:Wikihez/Gadget-test"; // A value carrying a namespace is used as the whole title; anything else is appended to BASE. function pageTitle(suffix) { return suffix.indexOf(":") !== -1 ? suffix : BASE + suffix; } // module name -> package ({main, files}) or plain script ({script}), plus styles. // deferred held back until something asks for it, as production fetches it only then // start asked for on page load, as production loads a default gadget // deps only for a gadget not yet in Gadgets-definition; a registered one keeps the wiki's // actions with start, only on these wgAction values, as a definition's "actions=" does var MODULES = { "ext.gadget.wikt.edit": { main: "wikt.edit.js", files: { "wikt.edit.js": "/wikt-edit.js", "wikt.edit/util.js": "/util.js", "wikt.edit/wikitext.js": "/wikitext.js", "wikt.edit/langs.js": "/langs.js", "wikt.edit/labels.js": "/labels.js", "wikt.edit/posparams.js": "/posparams.js", "wikt.edit/data-main.json": "/data-main.json", "wikt.edit/ziman-core.json": "/ziman-core.json" }, styles: ["/wikt-edit.css"] }, // Its own page, not a Gadget-test subpage: a value carrying a namespace is used as the title. "ext.gadget.wikt.add-examples": { deferred: true, main: "wikt.add-examples.js", files: { "wikt.add-examples.js": "Bikarhêner:Wikihez/Gadget-wikt.add-examples.js" }, styles: ["Bikarhêner:Wikihez/Gadget-wikt.add-examples.css"] }, // Turn the site's "wikt.dsb" off in Preferences while this is armed, or the wiki's copy wins. "ext.gadget.wikt.dsb": { start: true, main: "wikt.dsb.js", files: { "wikt.dsb.js": "/dsb.js" }, styles: ["/dsb.css"] }, // The launcher (the site's "start" gadget) asks for the wizard on the first click. "ext.gadget.wikt.start-wizard": { deferred: true, main: "wikt.start-wizard.js", files: { "wikt.start-wizard.js": ".js" }, styles: [".css"] }, // Where the sense panels live: dsb.js asks for it on the first pencil click. "ext.gadget.wikt.dsb-panels": { deferred: true, main: "wikt.dsb-panels.js", files: { "wikt.dsb-panels.js": "/dsb-panels.js" }, styles: ["/dsb-panels.css"] }, // Not in Gadgets-definition yet, so deps are given here. "ext.gadget.wikt.translation-adder": { start: true, actions: ["view"], deps: ["mediawiki.util", "mediawiki.api", "mediawiki.storage", "ext.gadget.StorageUtils"], main: "wikt.translation-adder.js", files: { "wikt.translation-adder.js": "/translation-adder.js", "wikt.translation-adder/editor.js": "/translation-adder/editor.js", "wikt.translation-adder/language-utils.js": "/translation-adder/language-utils.js", "wikt.translation-adder/data.js": "/translation-adder/data.js" }, styles: ["/translation-adder.css"] }, "ext.gadget.wikt.cite": { main: "wikt.cite.js", files: { "wikt.cite.js": "/cite.js", "wikt.cite/etimon.js": "/etimon.js" }, styles: ["/cite.css"] } }; // Force dataType "text": otherwise jQuery sees action=raw's JavaScript Content-Type and runs it. // maxage/smaxage=0 and cache:false because the browser otherwise serves a cached copy of the page, // and a test run against yesterday's file looks like a bug in today's. function raw(suffix) { return $.get({ url: mw.util.getUrl(pageTitle(suffix), { action: "raw", maxage: 0, smaxage: 0 }), dataType: "text", cache: false }); } function run() { var names = Object.keys(MODULES); // A module the wiki has started loading cannot be implemented a second time. var busy = names.filter(function (name) { var state = mw.loader.getState(name); return state !== null && state !== "registered"; }); if (busy.length) { console.warn("loader.js: already loaded from the wiki, so the site's copy runs: " + busy.join(", ")); mw.notify("loader.js: van amûran di Tercîhan de bigire, da ku kopiya ceribandinê bixebite: " + busy.map(function (n) { return n.replace("ext.gadget.", ""); }).join(", "), { type: "warn" }); } var ours = names.filter(function (name) { return busy.indexOf(name) === -1; }); // Every distinct page this harness needs, fetched once each. var suffixes = []; function need(suffix) { if (suffixes.indexOf(suffix) === -1) { suffixes.push(suffix); } } ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { need(m.script); } else { Object.keys(m.files).forEach(function (key) { need(m.files[key]); }); } m.styles.forEach(need); }); if (!suffixes.length) { return; } $.when.apply($, suffixes.map(raw)).done(function () { var args = suffixes.length === 1 ? [arguments] : [].slice.call(arguments); var text = {}; suffixes.forEach(function (s, i) { text[s] = args[i][0]; }); // Styles go in directly: a stylesheet passed to implement() that never fires its load event // leaves the module stuck in "executing", and everything queued behind it with it. function addStyles(styles) { (styles || []).forEach(function (suffix) { if (text[suffix]) { mw.util.addCSS(text[suffix]); } }); } // Kept from before the patch below, so requestSiteDeps() does not re-enter it. var origUsing = mw.loader.using; var bad = null; var payloads = {}; ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { payloads[name] = new Function("$", "jQuery", "require", "module", text[m.script]); return; } var files = {}; Object.keys(m.files).forEach(function (key) { var src = text[m.files[key]]; if (key.slice(-5) === ".json") { try { files[key] = JSON.parse(src); } catch (e) { bad = m.files[key] + " nederbasdar e (JSON parse ket): " + e.message; } return; } files[key] = new Function("require", "module", "exports", src); }); payloads[name] = { main: m.main, files: files }; }); if (bad) { mw.notify(bad, { type: "error" }); return; } // implement() runs a module as soon as its dependencies are ready, so a deferred one is // handed over only when something asks for it. function implement(name) { if (!payloads[name]) { return; } var payload = payloads[name]; delete payloads[name]; if (mw.loader.getState(name) === null) { mw.loader.register(name, "0", MODULES[name].deps || []); } else if (mw.loader.getState(name) !== "registered") { console.warn("loader.js: " + name + " is already loading from the wiki; its copy runs."); return; } mw.loader.implement(name, payload); addStyles(MODULES[name].styles); requestSiteDeps(name); } // ResourceLoader fetches a module only when something asks for it, and implement() is not // asking: a gadget handed over here waits at "loaded" for good on a dependency the harness // does not carry and nobody requested -- ext.gadget.citoid-helper behind wikt.cite, say, and // wikt.dsb-panels behind that. Its own copies are left out; those are implemented, not fetched. function requestSiteDeps(name) { var entry = mw.loader.moduleRegistry[name]; var deps = ((entry && entry.dependencies) || []).filter(function (dep) { return !MODULES[dep]; }); if (deps.length) { origUsing.call(mw.loader, deps); } } // A deferred module reached as another's dependency is handed over too, or ResourceLoader // fetches the site's copy of it instead. function withDeps(list) { var out = []; (function walk(items) { (items || []).forEach(function (n) { if (out.indexOf(n) !== -1) { return; } out.push(n); var entry = mw.loader.moduleRegistry[n]; walk(entry ? entry.dependencies : MODULES[n] && MODULES[n].deps); }); }(list)); return out; } mw.loader.using = function (modules) { withDeps([].concat(modules || [])).forEach(implement); return origUsing.apply(this, arguments); }; ours.forEach(function (name) { if (!MODULES[name].deferred) { implement(name); } }); var action = mw.config.get("wgAction"); var starting = ours.filter(function (name) { var m = MODULES[name]; return m.start && (!m.actions || m.actions.indexOf(action) !== -1); }); var settled = false; mw.loader.using(starting).then(function () { settled = true; console.log("loader.js: started " + starting.join(", ")); }, function (err) { settled = true; mw.notify("Xeta barkirinê: " + err, { type: "error" }); }); // A dependency stuck half-loaded leaves using() pending forever, with no error at all. setTimeout(function () { if (settled) { return; } var stuck = withDeps(starting) .filter(function (m) { return mw.loader.getState(m) !== "ready"; }) .map(function (m) { return m + "=" + mw.loader.getState(m); }); mw.notify("Barkirin asê ma: " + stuck.join(", "), { type: "warn" }); }, 10000); }).fail(function () { mw.notify("Nikare hemû rûpelên " + BASE + " bixwîne: " + suffixes.join(", "), { type: "error" }); }); } // The test gadget declares no dependencies, so mw.util is not there yet when this runs. mw.loader.using("mediawiki.util").then(run); })(); // </nowiki> bh64jebxal5qjuwthfh2say8hqffaet 6794037 6794005 2026-10-14T19:20:53Z Wikihez 7344 Load the translation adder on demand and the targeted translations gadget 6794037 javascript text/javascript // <nowiki> // Test harness for the gadgets. Loaded from [[MediaWiki:Gadget-test.js]], which is one line: // mw.loader.load('//ku.wiktionary.org/w/index.php?title=Bikarhêner:Wikihez/Gadget-test/loader.js&action=raw&ctype=text/javascript'); // // Runs the userspace copies in place of the site's gadgets, under the same module names. A gadget the // wiki has registered but not yet loaded is implemented here from the test page instead, so its // dependencies are the real ones from [[MediaWiki:Gadgets-definition]]. One already loading from the wiki // cannot be replaced: turn the default gadgets that load it (start and wikt.dsb load wikt.core) off in // Special:Preferences while testing, and this names any that are still on. // // MODULES lists only the gadgets with changes not yet live; everything else runs the site's copy. A // gadget is added back when it is being changed, and taken out once that change is deployed. (function () { "use strict"; // Set synchronously, before add-examples' DOM-ready pass, so its own inline link is never built. window.wikt = window.wikt || {}; wikt.addExamples = wikt.addExamples || {}; wikt.addExamples.suppressLinks = true; // Subpage segments are case-sensitive, so BASE must match the pages exactly. var BASE = "Bikarhêner:Wikihez/Gadget-test"; // A value carrying a namespace is used as the whole title; anything else is appended to BASE. function pageTitle(suffix) { return suffix.indexOf(":") !== -1 ? suffix : BASE + suffix; } // module name -> package ({main, files}) or plain script ({script}), plus styles. // deferred held back until something asks for it, as production fetches it only then // start asked for on page load, as production loads a default gadget // deps only for a gadget not yet in Gadgets-definition; a registered one keeps the wiki's // actions with start, only on these wgAction values, as a definition's "actions=" does var MODULES = { "ext.gadget.wikt.edit": { main: "wikt.edit.js", files: { "wikt.edit.js": "/wikt-edit.js", "wikt.edit/util.js": "/util.js", "wikt.edit/wikitext.js": "/wikitext.js", "wikt.edit/langs.js": "/langs.js", "wikt.edit/labels.js": "/labels.js", "wikt.edit/posparams.js": "/posparams.js", "wikt.edit/data-main.json": "/data-main.json", "wikt.edit/ziman-core.json": "/ziman-core.json" }, styles: ["/wikt-edit.css"] }, // Its own page, not a Gadget-test subpage: a value carrying a namespace is used as the title. "ext.gadget.wikt.add-examples": { deferred: true, main: "wikt.add-examples.js", files: { "wikt.add-examples.js": "Bikarhêner:Wikihez/Gadget-wikt.add-examples.js" }, styles: ["Bikarhêner:Wikihez/Gadget-wikt.add-examples.css"] }, // Turn the site's "wikt.dsb" off in Preferences while this is armed, or the wiki's copy wins. "ext.gadget.wikt.dsb": { start: true, main: "wikt.dsb.js", files: { "wikt.dsb.js": "/dsb.js" }, styles: ["/dsb.css"] }, // The launcher (the site's "start" gadget) asks for the wizard on the first click. "ext.gadget.wikt.start-wizard": { deferred: true, main: "wikt.start-wizard.js", files: { "wikt.start-wizard.js": ".js" }, styles: [".css"] }, // Where the sense panels live: dsb.js asks for it on the first pencil click. "ext.gadget.wikt.dsb-panels": { deferred: true, main: "wikt.dsb-panels.js", files: { "wikt.dsb-panels.js": "/dsb-panels.js" }, styles: ["/dsb-panels.css"] }, // Not in Gadgets-definition yet, so deps are given here. dsb.js asks for it from a Werger pencil. "ext.gadget.wikt.translation-adder": { deferred: true, deps: ["mediawiki.util", "mediawiki.api", "mediawiki.storage", "ext.gadget.StorageUtils"], main: "wikt.translation-adder.js", files: { "wikt.translation-adder.js": "/translation-adder.js", "wikt.translation-adder/editor.js": "/translation-adder/editor.js", "wikt.translation-adder/language-utils.js": "/translation-adder/language-utils.js", "wikt.translation-adder/data.js": "/translation-adder/data.js" }, styles: ["/translation-adder.css"] }, "ext.gadget.wikt.targeted-translations": { start: true, actions: ["view"], deps: ["mediawiki.storage"], script: "/targeted-translations.js", styles: ["/targeted-translations.css"] }, "ext.gadget.wikt.cite": { main: "wikt.cite.js", files: { "wikt.cite.js": "/cite.js", "wikt.cite/etimon.js": "/etimon.js" }, styles: ["/cite.css"] } }; // Force dataType "text": otherwise jQuery sees action=raw's JavaScript Content-Type and runs it. // maxage/smaxage=0 and cache:false because the browser otherwise serves a cached copy of the page, // and a test run against yesterday's file looks like a bug in today's. function raw(suffix) { return $.get({ url: mw.util.getUrl(pageTitle(suffix), { action: "raw", maxage: 0, smaxage: 0 }), dataType: "text", cache: false }); } function run() { var names = Object.keys(MODULES); // A module the wiki has started loading cannot be implemented a second time. var busy = names.filter(function (name) { var state = mw.loader.getState(name); return state !== null && state !== "registered"; }); if (busy.length) { console.warn("loader.js: already loaded from the wiki, so the site's copy runs: " + busy.join(", ")); mw.notify("loader.js: van amûran di Tercîhan de bigire, da ku kopiya ceribandinê bixebite: " + busy.map(function (n) { return n.replace("ext.gadget.", ""); }).join(", "), { type: "warn" }); } var ours = names.filter(function (name) { return busy.indexOf(name) === -1; }); // Every distinct page this harness needs, fetched once each. var suffixes = []; function need(suffix) { if (suffixes.indexOf(suffix) === -1) { suffixes.push(suffix); } } ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { need(m.script); } else { Object.keys(m.files).forEach(function (key) { need(m.files[key]); }); } m.styles.forEach(need); }); if (!suffixes.length) { return; } $.when.apply($, suffixes.map(raw)).done(function () { var args = suffixes.length === 1 ? [arguments] : [].slice.call(arguments); var text = {}; suffixes.forEach(function (s, i) { text[s] = args[i][0]; }); // Styles go in directly: a stylesheet passed to implement() that never fires its load event // leaves the module stuck in "executing", and everything queued behind it with it. function addStyles(styles) { (styles || []).forEach(function (suffix) { if (text[suffix]) { mw.util.addCSS(text[suffix]); } }); } // Kept from before the patch below, so requestSiteDeps() does not re-enter it. var origUsing = mw.loader.using; var bad = null; var payloads = {}; ours.forEach(function (name) { var m = MODULES[name]; if (m.script) { payloads[name] = new Function("$", "jQuery", "require", "module", text[m.script]); return; } var files = {}; Object.keys(m.files).forEach(function (key) { var src = text[m.files[key]]; if (key.slice(-5) === ".json") { try { files[key] = JSON.parse(src); } catch (e) { bad = m.files[key] + " nederbasdar e (JSON parse ket): " + e.message; } return; } files[key] = new Function("require", "module", "exports", src); }); payloads[name] = { main: m.main, files: files }; }); if (bad) { mw.notify(bad, { type: "error" }); return; } // implement() runs a module as soon as its dependencies are ready, so a deferred one is // handed over only when something asks for it. function implement(name) { if (!payloads[name]) { return; } var payload = payloads[name]; delete payloads[name]; if (mw.loader.getState(name) === null) { mw.loader.register(name, "0", MODULES[name].deps || []); } else if (mw.loader.getState(name) !== "registered") { console.warn("loader.js: " + name + " is already loading from the wiki; its copy runs."); return; } mw.loader.implement(name, payload); addStyles(MODULES[name].styles); requestSiteDeps(name); } // ResourceLoader fetches a module only when something asks for it, and implement() is not // asking: a gadget handed over here waits at "loaded" for good on a dependency the harness // does not carry and nobody requested -- ext.gadget.citoid-helper behind wikt.cite, say, and // wikt.dsb-panels behind that. Its own copies are left out; those are implemented, not fetched. function requestSiteDeps(name) { var entry = mw.loader.moduleRegistry[name]; var deps = ((entry && entry.dependencies) || []).filter(function (dep) { return !MODULES[dep]; }); if (deps.length) { origUsing.call(mw.loader, deps); } } // A deferred module reached as another's dependency is handed over too, or ResourceLoader // fetches the site's copy of it instead. function withDeps(list) { var out = []; (function walk(items) { (items || []).forEach(function (n) { if (out.indexOf(n) !== -1) { return; } out.push(n); var entry = mw.loader.moduleRegistry[n]; walk(entry ? entry.dependencies : MODULES[n] && MODULES[n].deps); }); }(list)); return out; } mw.loader.using = function (modules) { withDeps([].concat(modules || [])).forEach(implement); return origUsing.apply(this, arguments); }; ours.forEach(function (name) { if (!MODULES[name].deferred) { implement(name); } }); var action = mw.config.get("wgAction"); var starting = ours.filter(function (name) { var m = MODULES[name]; return m.start && (!m.actions || m.actions.indexOf(action) !== -1); }); var settled = false; mw.loader.using(starting).then(function () { settled = true; console.log("loader.js: started " + starting.join(", ")); }, function (err) { settled = true; mw.notify("Xeta barkirinê: " + err, { type: "error" }); }); // A dependency stuck half-loaded leaves using() pending forever, with no error at all. setTimeout(function () { if (settled) { return; } var stuck = withDeps(starting) .filter(function (m) { return mw.loader.getState(m) !== "ready"; }) .map(function (m) { return m + "=" + mw.loader.getState(m); }); mw.notify("Barkirin asê ma: " + stuck.join(", "), { type: "warn" }); }, 10000); }).fail(function () { mw.notify("Nikare hemû rûpelên " + BASE + " bixwîne: " + suffixes.join(", "), { type: "error" }); }); } // The test gadget declares no dependencies, so mw.util is not there yet when this runs. mw.loader.using("mediawiki.util").then(run); })(); // </nowiki> gb5kir7ncx0g37ez9who1q3ruzk2u39 Bikarhêner:Wikihez/Gadget-test/dsb.js 2 1358907 6794035 6531636 2026-10-14T19:20:42Z Wikihez 7344 DefSideBoxes: Werger pencil opens the translation adder, whose changes join the save queue 6794035 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.dsb.js]] * * The part of DefSideBoxes that runs on a page view: finds the definition lines and hangs a pencil on * each. Forms, dialogs and saves are in wikt.dsb-panels, fetched on the first click (never on hover). * * The "Nimûne/jêgirtin" item drives add-examples, which wikt.dsb-panels depends on -- so it is loaded by * the time any panel exists, and never on a plain page view. */ (function () { "use strict"; var core = require("ext.gadget.wikt.core"); var sections = core.sections; var MANE_SECTION = sections.MANE_SECTION; var PAGE_SUBSECTIONS = sections.PAGE_SUBSECTIONS; var msg = core.msg; var headingOuter = sections.headingOuter; var headingDepth = sections.headingDepth; var headingText = sections.headingText; var isManeHeading = sections.isManeHeading; var sectionTypeOf = sections.sectionTypeOf; var sectionOrdinalWithinLanguage = sections.sectionOrdinalWithinLanguage; var headingTextOrdinalWithinLanguage = sections.headingTextOrdinalWithinLanguage; var languageCodeForOl = sections.languageCodeForOl; var eachLeafDefinition = sections.eachLeafDefinition; if (mw.config.get("wgAction") !== "view") { return; } // An entry, a reconstruction, or a sandbox -- see sections.isEntryPage(). if (!sections.isEntryPage()) { return; } if (/&printable=yes|&diff=|&oldid=/.test(window.location.search)) { return; } window.wikt = window.wikt || {}; if (window.wikt.defSideBoxesLoaded) { return; } window.wikt.defSideBoxesLoaded = true; // A save from the queue box reloads with core's post-edit flag set (see dsb-panels.js). Core loads the // module that reads the flag only after a form save, so it is loaded here; it shows the toast and clears the flag. if (mw.storage.session.get("mw-PostEdit" + mw.config.get("wgPageName"))) { mw.loader.load("mediawiki.action.view.postEdit"); } // The panels are a separate module so their ~100 KB never lands on a page view. Fetched once, on the // first click that actually needs a form -- never on hover; every later call reuses the same promise. var panelsPromise = null; var panelsApi = null; // A panel is built once and merely shown again after that, so the reveal cannot live in the builders. // Only click handlers call this -- hover must never pull the panels module. function revealQueue() { withPanels(function (panels) { panels.openQueueBox(); }); } function withPanels(fn) { if (!panelsPromise) { panelsPromise = mw.loader.using("ext.gadget.wikt.dsb-panels").then(function (require) { panelsApi = require("ext.gadget.wikt.dsb-panels"); return panelsApi; }); } panelsPromise.then(fn, function () { mw.notify("Nikare amûrên guhartinê bar bike.", { type: "error" }); }); } // The element holding an example's last line of text: a quotation's source line and its quote are // blocks, so a pencil after them would sit on a line of its own. function lastTextLine(el) { var last = el.lastElementChild; while (last && /^(DIV|DL|DD|UL|OL|LI)$/.test(last.tagName)) { el = last; last = el.lastElementChild; } return el; } // A pencil on every example and quotation under a sense, opening add-examples' form on that one line. function attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3) { // A queued change redraws the sense (dsb-panels.js raises this); its examples get their pencils again. if (!$li.data("dsbExampleRedraw")) { $li.data("dsbExampleRedraw", true); $li.on("dsbSenseRedrawn", function () { attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3); }); } sections.exampleItemsOf($li).each(function () { var item = this; if ($(item).hasClass("dsb-has-example-toolbar")) { return; } var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Nimûneyê biguhêre"); var $toolbar = $("<span class='dsb-example-toolbar'></span>").append($toggle); $toggle.on("click", function (e) { e.preventDefault(); e.stopPropagation(); withPanels(function (panels) { panels.editExample($li, $h3, language, sectionType, sectionOrdinal, definitionLevel, item); }); }); // Touch has no hover: a tap on the example shows its pencil, as a tap on a sense does. $(lastTextLine(item)).append($toolbar); $(item).addClass("dsb-has-example-toolbar").on("click.dsbReveal", function () { $toolbar.addClass("dsb-revealed"); }); }); } // How many rows the pencil is currently holding open. A pinned row means somebody is part-way through // an edit, and opening another one under the pointer moves the page: the content below shifts, the // pointer ends up over something else, that fires its own hover, and the page jumps back and forth. // So while anything is pinned, hovering opens nothing. var pinnedRows = 0; var canHover = !window.matchMedia || window.matchMedia("(hover: hover)").matches; function attachToolbar($li, language, sectionType, sectionOrdinal, definitionLevel, $h3, isLastSense) { if ($li.children(".dsb-toolbar").length) { return; } var $toolbar = $("<span class='dsb-toolbar'></span>"); // The real InterlanguageLinks pencil icon (same asset defEditButton uses); image swap is in the CSS. var $toggle = $("<a href='#' class='dsb-toggle' title='Guhartina pêşketî'></a>"); // Revealed by $toggle -- an indented panel under the sense, not always visible. Built lazily on // first open rather than eagerly for every sense on the page. var $quickRow = $("<div class='dsb-quick-row'></div>").hide(); var pinned = false; // the pencil pins the row open; hovering alone does not var hovering = false; // Builds the row -- fetching the panels module the first time -- then calls back. Asynchronous now, // so the callback re-checks that the row is still wanted rather than showing it unconditionally. function ensureBuilt(then) { if ($quickRow.data("built")) { then(); return; } withPanels(function (panels) { if (!$quickRow.data("built")) { panels.buildAdvancedPanel($quickRow, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense); $quickRow.data("built", true); } then(); }); } // Anything the user has started here: an open field box, the open dropdown, or focus inside the row. function engaged() { return pinned || $quickRow.find(".dsb-open-boxes").children().length > 0 || $quickRow.find(".dsb-more-menu").is(":visible") || $.contains($quickRow.get(0), document.activeElement); } function setPinned(next) { if (pinned === next) { return; } pinned = next; pinnedRows += next ? 1 : -1; } $toggle.on("click", function (e) { e.preventDefault(); // Through their own cancel handler rather than a bare hide(), so a row this closes also drops // its pinned state -- hiding it alone would leave pinnedRows counting a row nobody can see. $(".dsb-quick-row").not($quickRow).trigger("dsbCancel"); setPinned(!pinned); if (pinned) { ensureBuilt(function () { if (pinned) { $quickRow.show(); } }); } else { $quickRow.hide(); } }); // Hover reveals the row and takes it away again, the way en.wikt's does -- but never while something // on it is in use, or a half-typed box would vanish on the way to the mouse leaving. $li.on("mouseenter.dsbHover", function () { // iOS plays a tap as mouseenter first, and a row shown here would swallow that tap's click. if (!canHover) { return; } hovering = true; // A row the pencil is holding open is already open, and no row opens on hover while another // one is: that is the case where the page is being edited and must stay still. if (pinned || pinnedRows) { return; } // Hover reveals a row that already exists; it never fetches the panels module. Otherwise // simply moving the pointer across an entry would pull ~100 KB on every page view, which is // the whole cost the split exists to avoid. The pencil is what asks for it. if ($quickRow.data("built")) { $quickRow.show(); } }); // Raised by the panel's "Betal bike" (see dsb-panels.js): clear the pinned state as well as // hiding, so one click on the pencil opens it again. $quickRow.on("dsbCancel", function () { setPinned(false); hovering = false; $quickRow.hide(); }); $li.on("mouseleave.dsbHover", function () { hovering = false; if (!engaged()) { $quickRow.hide(); } }); $toolbar.append($toggle); // The class positions the <li>, so the toolbar's gutter slot is measured from the line itself and // not from wherever it happens to sit in the child order -- ShowIDs prepends its id pill to this // same <li>. $li.addClass("dsb-def").prepend($toolbar); // Touch has no :hover -- a tap anywhere on the line reveals the pencil the same way hovering does on // desktop (CSS gates this to touch devices; harmless if it also fires on a mouse click). $li.on("click.dsbReveal", function () { $toolbar.addClass("dsb-revealed"); }); // Land $quickRow right after the sense's own gloss text -- before any existing "Hevmane: ..." <dl>, // citation <ul>, or the Nimûne/jêgirtin proxy link's own <ul>, not appended after all of that. var $firstBlock = $li.children("dl, ul, ol").first(); if ($firstBlock.length) { $firstBlock.before($quickRow); } else { $li.append($quickRow); } } // A section the data marks hidden is left alone. function isHiddenSection(header) { return PAGE_SUBSECTIONS.some(function (s) { return s.hidden && s.header === header; }); } // The translation section, whose pencil opens the translation adder on its boxes. function isWergerSection(header) { return PAGE_SUBSECTIONS.some(function (s) { return s.kind === "werger" && s.header === header; }); } // Everything between a heading and the next heading at its level or above. function sectionBody($heading) { var depth = headingDepth($heading); var $body = $(); for (var $cursor = $heading.next(); $cursor.length; $cursor = $cursor.next()) { var d = headingDepth($cursor); if (d && d <= depth) { break; } $body = $body.add($cursor); } return $body; } // The pencil on "==== Werger ====" shows a form under each translation box and a ± on its heading, // from [[MediaWiki:Gadget-wikt.translation-adder.js]]; the changes join the save queue. function attachWergerEditor($hh, $heading, $h3, header, language, sectionType) { var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " biguhêre"); var attached = null; $toolbar.append($toggle); placeHeadingToolbar($hh, $toolbar); revealHeadingToolbarOnTap($hh, $toolbar); $toggle.on("click", function (e) { e.preventDefault(); var $body = sectionBody($heading); var open = !$toolbar.hasClass("dsb-werger-open"); $toolbar.toggleClass("dsb-werger-open", open).toggleClass("dsb-revealed", open); $body.find(".translation-adder-row, .translation-adder-gloss").toggle(open); if (!open || attached) { return; } if (!$body.find("table.translations").length) { mw.notify("Di vê beşê de qutiyeke tercimeyan a ku amûr dikare bixwîne tune.", { type: "warn" }); return; } // Collapsed boxes are opened, so the forms are in sight. $body.find(".NavFrame").each(function () { if ($(this).children(".NavContent").is(":hidden")) { $(this).find(".NavToggle").first().trigger("click"); } }); withPanels(function (panels) { attached = mw.loader.using("ext.gadget.wikt.translation-adder").then(function (require) { return require("ext.gadget.wikt.translation-adder") .attach($body, panels.translationEditor($h3, language, sectionType)); }, function () { attached = null; mw.notify("Nikare amûra tercimeyan bar bike.", { type: "error" }); }); panels.openQueueBox(); }); }); } // Every subsection under a part of speech, whether or not the data knows the header: one it does not // know opens as wikitext, or as a word list when that is what it holds -- see buildSubsectionPanel. function attachSubsectionEditors($h3, language, sectionType, sectionOrdinal, posDepth) { posDepth = posDepth || 3; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var depth = headingDepth($cursor); if (depth && depth <= posDepth) { break; } if (depth !== posDepth + 1) { continue; } var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); var header = headingText($hh); if (!header || $hh.data("dsbAttached")) { continue; } if (isWergerSection(header)) { $hh.data("dsbAttached", true); attachWergerEditor($hh, $cursor, $h3, header, language, sectionType); continue; } if (isHiddenSection(header)) { continue; } $hh.data("dsbAttached", true); attachSubsectionEditor($hh, $cursor, $h3, header, language, sectionType, sectionOrdinal); } } // Where the pencil goes depends on what the skin does with its own edit links. Desktop skins put // "[biguhêre]" right after the heading text, so the pencil follows it and the two read as one group. // Minerva parks its edit icon at the far right of the heading row instead, a screen-width away from the // text, so there the pencil goes inside the heading rather than chasing it. function placeHeadingToolbar($heading, $toolbar) { if (mw.config.get("skin") !== "minerva") { var $editsection = headingOuter($heading).find(".mw-editsection").first(); if ($editsection.length) { $editsection.after($toolbar); return; } } $heading.append($toolbar); } // Touch has no hover, so a heading's pencil is shown by tapping the heading itself. Taps that mean // something else are left alone: the skin's own edit links, and the pencil once it is showing. function revealHeadingToolbarOnTap($heading, $toolbar) { headingOuter($heading).on("click", function (e) { if ($(e.target).closest("a, .dsb-toolbar").length) { return; } $toolbar.toggleClass("dsb-revealed"); }); } // One pencil's worth of behaviour, shared by the "====" headings and by an image's own caption: the // panel is built the first time it is opened, never before, and the pencil stays visible while it is. // free, when given, pins the panel to a heading located by its own text rather than through a part of // speech: {header, ordinal}. function bindPanelToggle($toggle, $toolbar, $panel, $h3, header, language, sectionType, sectionOrdinal, free) { $toggle.on("click", function (e) { e.preventDefault(); $panel.toggle(); // Stays put while its panel is open, so it does not vanish from under the pointer. $toolbar.toggleClass("dsb-revealed", $panel.is(":visible")); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal, header, free); }); } revealQueue(); }); } function attachSubsectionEditor($hh, $heading, $h3, header, language, sectionType, sectionOrdinal) { var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " biguhêre"); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($hh, $toolbar); revealHeadingToolbarOnTap($hh, $toolbar); $heading.after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h3, header, language, sectionType, sectionOrdinal); } // The "place" subsection that owns the thumbnails under a part-of-speech heading -- named in the data, // not spelled out here, so renaming it there renames it everywhere. var IMAGE_HEADER = (function () { var found = null; PAGE_SUBSECTIONS.forEach(function (s) { if (s.place === "pos-top") { found = s.header; } }); return found; }()); // A pencil on the image's own caption. A wrong caption is noticed while looking at the picture, and // this is where the writer reaches for it -- the same editor the section's own list offers, opened on // the image that is already there. // Every thumbnail between the heading and the first subsection, which is the run "pos-top" covers. // Anything else in that run is stepped over: a headword line, a declension table or the stylesheet a // template brings with it all sit before the pictures on plenty of entries. function attachImageEditors($h3, language, sectionType, sectionOrdinal) { if (!IMAGE_HEADER) { return; } for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if (headingDepth($cursor)) { break; } if ($cursor.is(".thumb, figure, .floatright, .floatleft")) { attachImageEditor($cursor, $h3, language, sectionType, sectionOrdinal); } } } // A "===" section that is not a part of speech but is one of ours: Bilêvkirin, which sits above the // part-of-speech blocks and belongs to the whole language. function placeSubsectionFor($h3) { var text = headingText($h3); var found = null; PAGE_SUBSECTIONS.forEach(function (s) { if (s.place === "pron" && s.header === text) { found = s; } }); return found; } // The first part-of-speech heading under the same language. The writer works from a section, so // without one there is nothing to anchor the box to. function firstPosHeadingAfter($h3) { for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is(".mw-heading2, h2")) { return null; // the next language starts } var depth = headingDepth($cursor); if (depth !== 3 && depth !== 4) { continue; } var $hh = $cursor.is("h3, h4") ? $cursor : $cursor.find("h3, h4").first(); if (sectionTypeOf($hh) || isManeHeading($hh)) { return $hh; } } return null; } // Its own pencil, opening the same box the section list offers -- a heading nobody can edit from the // page is a heading people edit by hand. function attachPlaceHeadingEditor($h3, subsection) { if ($h3.data("dsbAttached")) { return; } var $pos = firstPosHeadingAfter($h3); if (!$pos) { return; } var $ol = null; for (var $cursor = headingOuter($pos).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("ol")) { $ol = $cursor; break; } if ($cursor.is(".mw-heading2, .mw-heading3, h2, h3")) { break; } } var language = $ol && languageCodeForOl($ol); if (!language) { return; } $h3.data("dsbAttached", true); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", subsection.header + " " + msg.edit); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($h3, $toolbar); revealHeadingToolbarOnTap($h3, $toolbar); headingOuter($h3).after($panel); bindPanelToggle($toggle, $toolbar, $panel, $pos, subsection.header, language, sectionTypeOf($pos) || MANE_SECTION, sectionOrdinalWithinLanguage($pos)); } // Every other heading inside a language: the ones that sit beside the parts of speech rather than // under one ("=== Etîmolojî ===", "=== Çavkanî ==="), and the ones nobody planned for. Each opens // the wikitext under its own heading, or the {{kol3}} form when that is what it holds. function attachFreeSectionEditors() { $("#mw-content-text").find("h3, h4, h5, h6").each(function () { var $h = $(this); // A heading that already carries one of this gadget's pencils is spoken for. if ($h.data("dsbAttached") || headingOuter($h).find(".dsb-toolbar").length) { return; } var header = headingText($h); if (!header || isHiddenSection(header)) { return; } var language = languageCodeForOl($h); if (!language) { return; } $h.data("dsbAttached", true); attachFreeSectionEditor($h, header, language); }); } function attachFreeSectionEditor($h, header, language) { var free = { header: header, ordinal: headingTextOrdinalWithinLanguage($h) }; var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " " + msg.edit); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($h, $toolbar); revealHeadingToolbarOnTap($h, $toolbar); headingOuter($h).after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h, header, language, null, 0, free); } function attachImageEditor($fig, $h3, language, sectionType, sectionOrdinal) { if ($fig.data("dsbAttached")) { return; } var $caption = $fig.find("figcaption, .thumbcaption").first(); if (!$caption.length) { return; } $fig.data("dsbAttached", true); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-caption'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", IMAGE_HEADER + " biguhêre"); // After the figure, not inside it: the panel is full width, and a floated thumbnail is not. var $panel = $("<div class='dsb-subsection-panel dsb-panel-after-image'></div>").hide(); $toolbar.append($toggle); $caption.append($toolbar); revealHeadingToolbarOnTap($caption, $toolbar); $fig.after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h3, IMAGE_HEADER, language, sectionType, sectionOrdinal); } function attachSubsectionTrigger($h3, $ol, language, sectionType, sectionOrdinal) { if ($h3.data("dsbAttached")) { return; } $h3.data("dsbAttached", true); var $anchor = $ol; for (var $cursor = $ol.next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is(".mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6, h2, h3, h4, h5, h6")) { break; } $anchor = $cursor; } var $trigger = $("<a href='#' class='dsb-add-subsection'>+ Beşek lê zêde bike</a>"); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $trigger.on("click", function (e) { e.preventDefault(); $panel.toggle(); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal); }); } revealQueue(); }); $anchor.after($panel); $anchor.after($trigger); } // Attaches the "choose a part of speech" trigger under a heading whose part of speech is unsettled: an // unclassified "=== Mane ===" placeholder, or a "=== Biwêj ===" that is really a noun, verb, ... . function attachConvertPosTrigger($h3, sectionType) { if ($h3.data("dsbConvertAttached")) { return; } $h3.data("dsbConvertAttached", true); // languageCodeForOl() is really just "nearest preceding .besazimani[id] before this element in // document order" -- nothing ol-specific about it, so it works unchanged for a heading too. var language = languageCodeForOl($h3); if (!language) { return; } var sectionOrdinal = sectionOrdinalWithinLanguage($h3); // The same pencil the "====" subsections get, so reclassifying reads as an edit affordance rather // than a line of body text. var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Cureyê rêzimanî hilbijêre"); var $panel = $("<div class='dsb-convert-pos-panel'></div>").hide(); $toolbar.append($toggle); $toggle.on("click", function (e) { e.preventDefault(); $panel.toggle(); // Stays put while its panel is open, so it does not vanish from under the pointer. $toolbar.toggleClass("dsb-revealed", $panel.is(":visible")); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildConvertPosPanel($panel, $h3, language, sectionType, sectionOrdinal); }); } revealQueue(); }); // Raised by the panel's "Betal bike": closes it and drops what was typed, so the pencil reopens it fresh. $panel.on("dsbCancel", function () { $panel.hide().empty().data("built", false); $toolbar.removeClass("dsb-revealed"); }); placeHeadingToolbar($h3, $toolbar); revealHeadingToolbarOnTap($h3, $toolbar); headingOuter($h3).after($panel); } // ---- driver ---- function init() { $("h3, h4").each(function () { var $h3 = $(this); var posDepth = headingDepth($h3) || 3; var sectionType = sectionTypeOf($h3); if (!sectionType) { var place = placeSubsectionFor($h3); if (place) { attachPlaceHeadingEditor($h3, place); return; } if (!isManeHeading($h3)) { return; // not a recognized word-sense section (Etîmolojî, Çavkanî, ...) } // An unclassified placeholder: offer the POS picker, then carry on and wire up the same // per-sense pencils, so the POS choice and the sense edits can be queued and saved together. sectionType = MANE_SECTION; attachConvertPosTrigger($h3, sectionType); } else { // Every part of speech, not just the ones most often mistaken: any section can turn out to // be another, and the picker is what changes it. attachConvertPosTrigger($h3, sectionType); } var sectionOrdinal = sectionOrdinalWithinLanguage($h3); var $ol = null; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("ol")) { $ol = $cursor; break; } if (headingDepth($cursor)) { break; } } if (!$ol) { return; } var language = languageCodeForOl($ol); if (!language) { return; } var lastTopIndex = $ol.children("li").not(".mw-empty-elt").length - 1; eachLeafDefinition($ol, function ($li) { var definitionLevel = []; var $definitionItem = $li; while ($definitionItem.prop("tagName") === "LI") { definitionLevel.splice(0, 0, $definitionItem.prevAll("li:not(.mw-empty-elt)").length); $definitionItem = $definitionItem.parent().parent(); } definitionLevel.splice(0, 0, sectionType + "|" + sectionOrdinal); var isLastSense = definitionLevel.length === 2 && definitionLevel[1] === lastTopIndex; attachToolbar($li, language, sectionType, sectionOrdinal, definitionLevel, $h3, isLastSense); attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3); }); attachSubsectionTrigger($h3, $ol, language, sectionType, sectionOrdinal, posDepth); attachSubsectionEditors($h3, language, sectionType, sectionOrdinal, posDepth); attachImageEditors($h3, language, sectionType, sectionOrdinal); }); // Last, so a heading one of the walks above claimed keeps the pencil that knows more about it. attachFreeSectionEditors(); } // One listener for every "Hevmane û Bêhtir" dropdown on the page. $(document).on("click.dsbMoreMenu", function (e) { $(".dsb-more-menu:visible").each(function () { var $menu = $(this); if ($(e.target).closest($menu).length || $(e.target).closest($menu.prev(".dsb-more-toggle")).length) { return; } $menu.slideUp(); var $arrow = $menu.data("dsbArrow"); if ($arrow) { $arrow.text("▸"); } }); }); // Closes every open panel/preview this gadget can leave in the DOM, so bfcache never freezes one mid-open. function closeAllOpenUi() { if (panelsApi) { panelsApi.clearQueue(); panelsApi.closeAllPanels(); return; } // Before the panels arrive there is nothing built to tear down, only the view-path rows. $(".dsb-quick-row, .dsb-more-menu, .dsb-preview-inline, .dsb-preview-block").hide(); } $(window).on("pagehide", closeAllOpenUi); // Belt-and-suspenders: also close on restore-from-bfcache, in case pagehide's own hide didn't stick. $(window).on("pageshow", function (e) { if (e.originalEvent && e.originalEvent.persisted) { closeAllOpenUi(); } }); $(init); // </nowiki> }()); // </nowiki> 8czij6y1cflb7bjb45njog0d3zgjteo 6794045 6794035 2026-10-14T19:33:48Z Wikihez 7344 DefSideBoxes: translation forms shown on page view; Werger pencil edits existing lines; pending lines highlighted 6794045 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.dsb.js]] * * The part of DefSideBoxes that runs on a page view: finds the definition lines and hangs a pencil on * each. Forms, dialogs and saves are in wikt.dsb-panels, fetched on the first click (never on hover). * * The "Nimûne/jêgirtin" item drives add-examples, which wikt.dsb-panels depends on -- so it is loaded by * the time any panel exists, and never on a plain page view. */ (function () { "use strict"; var core = require("ext.gadget.wikt.core"); var sections = core.sections; var MANE_SECTION = sections.MANE_SECTION; var PAGE_SUBSECTIONS = sections.PAGE_SUBSECTIONS; var msg = core.msg; var headingOuter = sections.headingOuter; var headingDepth = sections.headingDepth; var headingText = sections.headingText; var isManeHeading = sections.isManeHeading; var sectionTypeOf = sections.sectionTypeOf; var sectionOrdinalWithinLanguage = sections.sectionOrdinalWithinLanguage; var headingTextOrdinalWithinLanguage = sections.headingTextOrdinalWithinLanguage; var languageCodeForOl = sections.languageCodeForOl; var eachLeafDefinition = sections.eachLeafDefinition; if (mw.config.get("wgAction") !== "view") { return; } // An entry, a reconstruction, or a sandbox -- see sections.isEntryPage(). if (!sections.isEntryPage()) { return; } if (/&printable=yes|&diff=|&oldid=/.test(window.location.search)) { return; } window.wikt = window.wikt || {}; if (window.wikt.defSideBoxesLoaded) { return; } window.wikt.defSideBoxesLoaded = true; // A save from the queue box reloads with core's post-edit flag set (see dsb-panels.js). Core loads the // module that reads the flag only after a form save, so it is loaded here; it shows the toast and clears the flag. if (mw.storage.session.get("mw-PostEdit" + mw.config.get("wgPageName"))) { mw.loader.load("mediawiki.action.view.postEdit"); } // The panels are a separate module so their ~100 KB never lands on a page view. Fetched once, on the // first click that actually needs a form -- never on hover; every later call reuses the same promise. var panelsPromise = null; var panelsApi = null; // A panel is built once and merely shown again after that, so the reveal cannot live in the builders. // Only click handlers call this -- hover must never pull the panels module. function revealQueue() { withPanels(function (panels) { panels.openQueueBox(); }); } function withPanels(fn) { if (!panelsPromise) { panelsPromise = mw.loader.using("ext.gadget.wikt.dsb-panels").then(function (require) { panelsApi = require("ext.gadget.wikt.dsb-panels"); return panelsApi; }); } panelsPromise.then(fn, function () { mw.notify("Nikare amûrên guhartinê bar bike.", { type: "error" }); }); } // The element holding an example's last line of text: a quotation's source line and its quote are // blocks, so a pencil after them would sit on a line of its own. function lastTextLine(el) { var last = el.lastElementChild; while (last && /^(DIV|DL|DD|UL|OL|LI)$/.test(last.tagName)) { el = last; last = el.lastElementChild; } return el; } // A pencil on every example and quotation under a sense, opening add-examples' form on that one line. function attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3) { // A queued change redraws the sense (dsb-panels.js raises this); its examples get their pencils again. if (!$li.data("dsbExampleRedraw")) { $li.data("dsbExampleRedraw", true); $li.on("dsbSenseRedrawn", function () { attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3); }); } sections.exampleItemsOf($li).each(function () { var item = this; if ($(item).hasClass("dsb-has-example-toolbar")) { return; } var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Nimûneyê biguhêre"); var $toolbar = $("<span class='dsb-example-toolbar'></span>").append($toggle); $toggle.on("click", function (e) { e.preventDefault(); e.stopPropagation(); withPanels(function (panels) { panels.editExample($li, $h3, language, sectionType, sectionOrdinal, definitionLevel, item); }); }); // Touch has no hover: a tap on the example shows its pencil, as a tap on a sense does. $(lastTextLine(item)).append($toolbar); $(item).addClass("dsb-has-example-toolbar").on("click.dsbReveal", function () { $toolbar.addClass("dsb-revealed"); }); }); } // How many rows the pencil is currently holding open. A pinned row means somebody is part-way through // an edit, and opening another one under the pointer moves the page: the content below shifts, the // pointer ends up over something else, that fires its own hover, and the page jumps back and forth. // So while anything is pinned, hovering opens nothing. var pinnedRows = 0; var canHover = !window.matchMedia || window.matchMedia("(hover: hover)").matches; function attachToolbar($li, language, sectionType, sectionOrdinal, definitionLevel, $h3, isLastSense) { if ($li.children(".dsb-toolbar").length) { return; } var $toolbar = $("<span class='dsb-toolbar'></span>"); // The real InterlanguageLinks pencil icon (same asset defEditButton uses); image swap is in the CSS. var $toggle = $("<a href='#' class='dsb-toggle' title='Guhartina pêşketî'></a>"); // Revealed by $toggle -- an indented panel under the sense, not always visible. Built lazily on // first open rather than eagerly for every sense on the page. var $quickRow = $("<div class='dsb-quick-row'></div>").hide(); var pinned = false; // the pencil pins the row open; hovering alone does not var hovering = false; // Builds the row -- fetching the panels module the first time -- then calls back. Asynchronous now, // so the callback re-checks that the row is still wanted rather than showing it unconditionally. function ensureBuilt(then) { if ($quickRow.data("built")) { then(); return; } withPanels(function (panels) { if (!$quickRow.data("built")) { panels.buildAdvancedPanel($quickRow, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense); $quickRow.data("built", true); } then(); }); } // Anything the user has started here: an open field box, the open dropdown, or focus inside the row. function engaged() { return pinned || $quickRow.find(".dsb-open-boxes").children().length > 0 || $quickRow.find(".dsb-more-menu").is(":visible") || $.contains($quickRow.get(0), document.activeElement); } function setPinned(next) { if (pinned === next) { return; } pinned = next; pinnedRows += next ? 1 : -1; } $toggle.on("click", function (e) { e.preventDefault(); // Through their own cancel handler rather than a bare hide(), so a row this closes also drops // its pinned state -- hiding it alone would leave pinnedRows counting a row nobody can see. $(".dsb-quick-row").not($quickRow).trigger("dsbCancel"); setPinned(!pinned); if (pinned) { ensureBuilt(function () { if (pinned) { $quickRow.show(); } }); } else { $quickRow.hide(); } }); // Hover reveals the row and takes it away again, the way en.wikt's does -- but never while something // on it is in use, or a half-typed box would vanish on the way to the mouse leaving. $li.on("mouseenter.dsbHover", function () { // iOS plays a tap as mouseenter first, and a row shown here would swallow that tap's click. if (!canHover) { return; } hovering = true; // A row the pencil is holding open is already open, and no row opens on hover while another // one is: that is the case where the page is being edited and must stay still. if (pinned || pinnedRows) { return; } // Hover reveals a row that already exists; it never fetches the panels module. Otherwise // simply moving the pointer across an entry would pull ~100 KB on every page view, which is // the whole cost the split exists to avoid. The pencil is what asks for it. if ($quickRow.data("built")) { $quickRow.show(); } }); // Raised by the panel's "Betal bike" (see dsb-panels.js): clear the pinned state as well as // hiding, so one click on the pencil opens it again. $quickRow.on("dsbCancel", function () { setPinned(false); hovering = false; $quickRow.hide(); }); $li.on("mouseleave.dsbHover", function () { hovering = false; if (!engaged()) { $quickRow.hide(); } }); $toolbar.append($toggle); // The class positions the <li>, so the toolbar's gutter slot is measured from the line itself and // not from wherever it happens to sit in the child order -- ShowIDs prepends its id pill to this // same <li>. $li.addClass("dsb-def").prepend($toolbar); // Touch has no :hover -- a tap anywhere on the line reveals the pencil the same way hovering does on // desktop (CSS gates this to touch devices; harmless if it also fires on a mouse click). $li.on("click.dsbReveal", function () { $toolbar.addClass("dsb-revealed"); }); // Land $quickRow right after the sense's own gloss text -- before any existing "Hevmane: ..." <dl>, // citation <ul>, or the Nimûne/jêgirtin proxy link's own <ul>, not appended after all of that. var $firstBlock = $li.children("dl, ul, ol").first(); if ($firstBlock.length) { $firstBlock.before($quickRow); } else { $li.append($quickRow); } } // A section the data marks hidden is left alone. function isHiddenSection(header) { return PAGE_SUBSECTIONS.some(function (s) { return s.hidden && s.header === header; }); } // The translation section, whose pencil opens the translation adder on its boxes. function isWergerSection(header) { return PAGE_SUBSECTIONS.some(function (s) { return s.kind === "werger" && s.header === header; }); } // Everything between a heading and the next heading at its level or above. function sectionBody($heading) { var depth = headingDepth($heading); var $body = $(); for (var $cursor = $heading.next(); $cursor.length; $cursor = $cursor.next()) { var d = headingDepth($cursor); if (d && d <= depth) { break; } $body = $body.add($cursor); } return $body; } // An editor for the translation adder that fetches the panels only when a change is first made, so a // page view with translation boxes does not pull them. function lazyTranslationEditor($h3, language, sectionType) { var real = null; function withEditor(fn) { withPanels(function (panels) { real = real || panels.translationEditor($h3, language, sectionType); panels.openQueueBox(); fn(real); }); } return { withCurrentText: function (callback) { withEditor(function (ed) { ed.withCurrentText(callback); }); }, addEdit: function (edit, node) { withEditor(function (ed) { ed.addEdit(edit, node); }); }, error: function (message) { mw.notify(message, { type: "error" }); }, undo: function () { withEditor(function (ed) { ed.undo(); }); } }; } // Under "==== Werger ====" every box gets the form for a new translation and a ± on its heading, from // [[MediaWiki:Gadget-wikt.translation-adder.js]]. The heading's pencil puts ✎ and × on the existing // lines. All their changes join the save queue. function attachWergerEditor($hh, $heading, $h3, header, language, sectionType) { var $body = sectionBody($heading); if (!$body.find("table.translations").length) { return; } var editor = lazyTranslationEditor($h3, language, sectionType); var adder = mw.loader.using("ext.gadget.wikt.translation-adder").then(function (require) { var api = require("ext.gadget.wikt.translation-adder"); return api.attach($body, editor).then(function () { return api; }); }); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Tercimeyên heyî biguhêre"); $toolbar.append($toggle); placeHeadingToolbar($hh, $toolbar); revealHeadingToolbarOnTap($hh, $toolbar); $toggle.on("click", function (e) { e.preventDefault(); var open = !$toolbar.hasClass("dsb-werger-open"); $toolbar.toggleClass("dsb-werger-open", open).toggleClass("dsb-revealed", open); if (open) { // Collapsed boxes are opened, so their lines are in sight. $body.find(".NavFrame").each(function () { if ($(this).children(".NavContent").is(":hidden")) { $(this).find(".NavToggle").first().trigger("click"); } }); } adder.then(function (api) { api.setLineEditing($body, editor, open); }, function () { mw.notify("Nikare amûra tercimeyan bar bike.", { type: "error" }); }); }); } // Every subsection under a part of speech, whether or not the data knows the header: one it does not // know opens as wikitext, or as a word list when that is what it holds -- see buildSubsectionPanel. function attachSubsectionEditors($h3, language, sectionType, sectionOrdinal, posDepth) { posDepth = posDepth || 3; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var depth = headingDepth($cursor); if (depth && depth <= posDepth) { break; } if (depth !== posDepth + 1) { continue; } var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); var header = headingText($hh); if (!header || $hh.data("dsbAttached")) { continue; } if (isWergerSection(header)) { $hh.data("dsbAttached", true); attachWergerEditor($hh, $cursor, $h3, header, language, sectionType); continue; } if (isHiddenSection(header)) { continue; } $hh.data("dsbAttached", true); attachSubsectionEditor($hh, $cursor, $h3, header, language, sectionType, sectionOrdinal); } } // Where the pencil goes depends on what the skin does with its own edit links. Desktop skins put // "[biguhêre]" right after the heading text, so the pencil follows it and the two read as one group. // Minerva parks its edit icon at the far right of the heading row instead, a screen-width away from the // text, so there the pencil goes inside the heading rather than chasing it. function placeHeadingToolbar($heading, $toolbar) { if (mw.config.get("skin") !== "minerva") { var $editsection = headingOuter($heading).find(".mw-editsection").first(); if ($editsection.length) { $editsection.after($toolbar); return; } } $heading.append($toolbar); } // Touch has no hover, so a heading's pencil is shown by tapping the heading itself. Taps that mean // something else are left alone: the skin's own edit links, and the pencil once it is showing. function revealHeadingToolbarOnTap($heading, $toolbar) { headingOuter($heading).on("click", function (e) { if ($(e.target).closest("a, .dsb-toolbar").length) { return; } $toolbar.toggleClass("dsb-revealed"); }); } // One pencil's worth of behaviour, shared by the "====" headings and by an image's own caption: the // panel is built the first time it is opened, never before, and the pencil stays visible while it is. // free, when given, pins the panel to a heading located by its own text rather than through a part of // speech: {header, ordinal}. function bindPanelToggle($toggle, $toolbar, $panel, $h3, header, language, sectionType, sectionOrdinal, free) { $toggle.on("click", function (e) { e.preventDefault(); $panel.toggle(); // Stays put while its panel is open, so it does not vanish from under the pointer. $toolbar.toggleClass("dsb-revealed", $panel.is(":visible")); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal, header, free); }); } revealQueue(); }); } function attachSubsectionEditor($hh, $heading, $h3, header, language, sectionType, sectionOrdinal) { var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " biguhêre"); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($hh, $toolbar); revealHeadingToolbarOnTap($hh, $toolbar); $heading.after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h3, header, language, sectionType, sectionOrdinal); } // The "place" subsection that owns the thumbnails under a part-of-speech heading -- named in the data, // not spelled out here, so renaming it there renames it everywhere. var IMAGE_HEADER = (function () { var found = null; PAGE_SUBSECTIONS.forEach(function (s) { if (s.place === "pos-top") { found = s.header; } }); return found; }()); // A pencil on the image's own caption. A wrong caption is noticed while looking at the picture, and // this is where the writer reaches for it -- the same editor the section's own list offers, opened on // the image that is already there. // Every thumbnail between the heading and the first subsection, which is the run "pos-top" covers. // Anything else in that run is stepped over: a headword line, a declension table or the stylesheet a // template brings with it all sit before the pictures on plenty of entries. function attachImageEditors($h3, language, sectionType, sectionOrdinal) { if (!IMAGE_HEADER) { return; } for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if (headingDepth($cursor)) { break; } if ($cursor.is(".thumb, figure, .floatright, .floatleft")) { attachImageEditor($cursor, $h3, language, sectionType, sectionOrdinal); } } } // A "===" section that is not a part of speech but is one of ours: Bilêvkirin, which sits above the // part-of-speech blocks and belongs to the whole language. function placeSubsectionFor($h3) { var text = headingText($h3); var found = null; PAGE_SUBSECTIONS.forEach(function (s) { if (s.place === "pron" && s.header === text) { found = s; } }); return found; } // The first part-of-speech heading under the same language. The writer works from a section, so // without one there is nothing to anchor the box to. function firstPosHeadingAfter($h3) { for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is(".mw-heading2, h2")) { return null; // the next language starts } var depth = headingDepth($cursor); if (depth !== 3 && depth !== 4) { continue; } var $hh = $cursor.is("h3, h4") ? $cursor : $cursor.find("h3, h4").first(); if (sectionTypeOf($hh) || isManeHeading($hh)) { return $hh; } } return null; } // Its own pencil, opening the same box the section list offers -- a heading nobody can edit from the // page is a heading people edit by hand. function attachPlaceHeadingEditor($h3, subsection) { if ($h3.data("dsbAttached")) { return; } var $pos = firstPosHeadingAfter($h3); if (!$pos) { return; } var $ol = null; for (var $cursor = headingOuter($pos).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("ol")) { $ol = $cursor; break; } if ($cursor.is(".mw-heading2, .mw-heading3, h2, h3")) { break; } } var language = $ol && languageCodeForOl($ol); if (!language) { return; } $h3.data("dsbAttached", true); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", subsection.header + " " + msg.edit); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($h3, $toolbar); revealHeadingToolbarOnTap($h3, $toolbar); headingOuter($h3).after($panel); bindPanelToggle($toggle, $toolbar, $panel, $pos, subsection.header, language, sectionTypeOf($pos) || MANE_SECTION, sectionOrdinalWithinLanguage($pos)); } // Every other heading inside a language: the ones that sit beside the parts of speech rather than // under one ("=== Etîmolojî ===", "=== Çavkanî ==="), and the ones nobody planned for. Each opens // the wikitext under its own heading, or the {{kol3}} form when that is what it holds. function attachFreeSectionEditors() { $("#mw-content-text").find("h3, h4, h5, h6").each(function () { var $h = $(this); // A heading that already carries one of this gadget's pencils is spoken for. if ($h.data("dsbAttached") || headingOuter($h).find(".dsb-toolbar").length) { return; } var header = headingText($h); if (!header || isHiddenSection(header)) { return; } var language = languageCodeForOl($h); if (!language) { return; } $h.data("dsbAttached", true); attachFreeSectionEditor($h, header, language); }); } function attachFreeSectionEditor($h, header, language) { var free = { header: header, ordinal: headingTextOrdinalWithinLanguage($h) }; var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " " + msg.edit); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($h, $toolbar); revealHeadingToolbarOnTap($h, $toolbar); headingOuter($h).after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h, header, language, null, 0, free); } function attachImageEditor($fig, $h3, language, sectionType, sectionOrdinal) { if ($fig.data("dsbAttached")) { return; } var $caption = $fig.find("figcaption, .thumbcaption").first(); if (!$caption.length) { return; } $fig.data("dsbAttached", true); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-caption'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", IMAGE_HEADER + " biguhêre"); // After the figure, not inside it: the panel is full width, and a floated thumbnail is not. var $panel = $("<div class='dsb-subsection-panel dsb-panel-after-image'></div>").hide(); $toolbar.append($toggle); $caption.append($toolbar); revealHeadingToolbarOnTap($caption, $toolbar); $fig.after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h3, IMAGE_HEADER, language, sectionType, sectionOrdinal); } function attachSubsectionTrigger($h3, $ol, language, sectionType, sectionOrdinal) { if ($h3.data("dsbAttached")) { return; } $h3.data("dsbAttached", true); var $anchor = $ol; for (var $cursor = $ol.next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is(".mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6, h2, h3, h4, h5, h6")) { break; } $anchor = $cursor; } var $trigger = $("<a href='#' class='dsb-add-subsection'>+ Beşek lê zêde bike</a>"); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $trigger.on("click", function (e) { e.preventDefault(); $panel.toggle(); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal); }); } revealQueue(); }); $anchor.after($panel); $anchor.after($trigger); } // Attaches the "choose a part of speech" trigger under a heading whose part of speech is unsettled: an // unclassified "=== Mane ===" placeholder, or a "=== Biwêj ===" that is really a noun, verb, ... . function attachConvertPosTrigger($h3, sectionType) { if ($h3.data("dsbConvertAttached")) { return; } $h3.data("dsbConvertAttached", true); // languageCodeForOl() is really just "nearest preceding .besazimani[id] before this element in // document order" -- nothing ol-specific about it, so it works unchanged for a heading too. var language = languageCodeForOl($h3); if (!language) { return; } var sectionOrdinal = sectionOrdinalWithinLanguage($h3); // The same pencil the "====" subsections get, so reclassifying reads as an edit affordance rather // than a line of body text. var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Cureyê rêzimanî hilbijêre"); var $panel = $("<div class='dsb-convert-pos-panel'></div>").hide(); $toolbar.append($toggle); $toggle.on("click", function (e) { e.preventDefault(); $panel.toggle(); // Stays put while its panel is open, so it does not vanish from under the pointer. $toolbar.toggleClass("dsb-revealed", $panel.is(":visible")); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildConvertPosPanel($panel, $h3, language, sectionType, sectionOrdinal); }); } revealQueue(); }); // Raised by the panel's "Betal bike": closes it and drops what was typed, so the pencil reopens it fresh. $panel.on("dsbCancel", function () { $panel.hide().empty().data("built", false); $toolbar.removeClass("dsb-revealed"); }); placeHeadingToolbar($h3, $toolbar); revealHeadingToolbarOnTap($h3, $toolbar); headingOuter($h3).after($panel); } // ---- driver ---- function init() { $("h3, h4").each(function () { var $h3 = $(this); var posDepth = headingDepth($h3) || 3; var sectionType = sectionTypeOf($h3); if (!sectionType) { var place = placeSubsectionFor($h3); if (place) { attachPlaceHeadingEditor($h3, place); return; } if (!isManeHeading($h3)) { return; // not a recognized word-sense section (Etîmolojî, Çavkanî, ...) } // An unclassified placeholder: offer the POS picker, then carry on and wire up the same // per-sense pencils, so the POS choice and the sense edits can be queued and saved together. sectionType = MANE_SECTION; attachConvertPosTrigger($h3, sectionType); } else { // Every part of speech, not just the ones most often mistaken: any section can turn out to // be another, and the picker is what changes it. attachConvertPosTrigger($h3, sectionType); } var sectionOrdinal = sectionOrdinalWithinLanguage($h3); var $ol = null; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("ol")) { $ol = $cursor; break; } if (headingDepth($cursor)) { break; } } if (!$ol) { return; } var language = languageCodeForOl($ol); if (!language) { return; } var lastTopIndex = $ol.children("li").not(".mw-empty-elt").length - 1; eachLeafDefinition($ol, function ($li) { var definitionLevel = []; var $definitionItem = $li; while ($definitionItem.prop("tagName") === "LI") { definitionLevel.splice(0, 0, $definitionItem.prevAll("li:not(.mw-empty-elt)").length); $definitionItem = $definitionItem.parent().parent(); } definitionLevel.splice(0, 0, sectionType + "|" + sectionOrdinal); var isLastSense = definitionLevel.length === 2 && definitionLevel[1] === lastTopIndex; attachToolbar($li, language, sectionType, sectionOrdinal, definitionLevel, $h3, isLastSense); attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3); }); attachSubsectionTrigger($h3, $ol, language, sectionType, sectionOrdinal, posDepth); attachSubsectionEditors($h3, language, sectionType, sectionOrdinal, posDepth); attachImageEditors($h3, language, sectionType, sectionOrdinal); }); // Last, so a heading one of the walks above claimed keeps the pencil that knows more about it. attachFreeSectionEditors(); } // One listener for every "Hevmane û Bêhtir" dropdown on the page. $(document).on("click.dsbMoreMenu", function (e) { $(".dsb-more-menu:visible").each(function () { var $menu = $(this); if ($(e.target).closest($menu).length || $(e.target).closest($menu.prev(".dsb-more-toggle")).length) { return; } $menu.slideUp(); var $arrow = $menu.data("dsbArrow"); if ($arrow) { $arrow.text("▸"); } }); }); // Closes every open panel/preview this gadget can leave in the DOM, so bfcache never freezes one mid-open. function closeAllOpenUi() { if (panelsApi) { panelsApi.clearQueue(); panelsApi.closeAllPanels(); return; } // Before the panels arrive there is nothing built to tear down, only the view-path rows. $(".dsb-quick-row, .dsb-more-menu, .dsb-preview-inline, .dsb-preview-block").hide(); } $(window).on("pagehide", closeAllOpenUi); // Belt-and-suspenders: also close on restore-from-bfcache, in case pagehide's own hide didn't stick. $(window).on("pageshow", function (e) { if (e.originalEvent && e.originalEvent.persisted) { closeAllOpenUi(); } }); $(init); // </nowiki> }()); // </nowiki> fs9xgtstnr3hqav8oc66kjqh921zqfq 6794089 6794045 2026-10-14T21:49:59Z Wikihez 7344 New translation goes after the last ✎ ×, and editing mode opens so it has its own 6794089 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.dsb.js]] * * The part of DefSideBoxes that runs on a page view: finds the definition lines and hangs a pencil on * each. Forms, dialogs and saves are in wikt.dsb-panels, fetched on the first click (never on hover). * * The "Nimûne/jêgirtin" item drives add-examples, which wikt.dsb-panels depends on -- so it is loaded by * the time any panel exists, and never on a plain page view. */ (function () { "use strict"; var core = require("ext.gadget.wikt.core"); var sections = core.sections; var MANE_SECTION = sections.MANE_SECTION; var PAGE_SUBSECTIONS = sections.PAGE_SUBSECTIONS; var msg = core.msg; var headingOuter = sections.headingOuter; var headingDepth = sections.headingDepth; var headingText = sections.headingText; var isManeHeading = sections.isManeHeading; var sectionTypeOf = sections.sectionTypeOf; var sectionOrdinalWithinLanguage = sections.sectionOrdinalWithinLanguage; var headingTextOrdinalWithinLanguage = sections.headingTextOrdinalWithinLanguage; var languageCodeForOl = sections.languageCodeForOl; var eachLeafDefinition = sections.eachLeafDefinition; if (mw.config.get("wgAction") !== "view") { return; } // An entry, a reconstruction, or a sandbox -- see sections.isEntryPage(). if (!sections.isEntryPage()) { return; } if (/&printable=yes|&diff=|&oldid=/.test(window.location.search)) { return; } window.wikt = window.wikt || {}; if (window.wikt.defSideBoxesLoaded) { return; } window.wikt.defSideBoxesLoaded = true; // A save from the queue box reloads with core's post-edit flag set (see dsb-panels.js). Core loads the // module that reads the flag only after a form save, so it is loaded here; it shows the toast and clears the flag. if (mw.storage.session.get("mw-PostEdit" + mw.config.get("wgPageName"))) { mw.loader.load("mediawiki.action.view.postEdit"); } // The panels are a separate module so their ~100 KB never lands on a page view. Fetched once, on the // first click that actually needs a form -- never on hover; every later call reuses the same promise. var panelsPromise = null; var panelsApi = null; // A panel is built once and merely shown again after that, so the reveal cannot live in the builders. // Only click handlers call this -- hover must never pull the panels module. function revealQueue() { withPanels(function (panels) { panels.openQueueBox(); }); } function withPanels(fn) { if (!panelsPromise) { panelsPromise = mw.loader.using("ext.gadget.wikt.dsb-panels").then(function (require) { panelsApi = require("ext.gadget.wikt.dsb-panels"); return panelsApi; }); } panelsPromise.then(fn, function () { mw.notify("Nikare amûrên guhartinê bar bike.", { type: "error" }); }); } // The element holding an example's last line of text: a quotation's source line and its quote are // blocks, so a pencil after them would sit on a line of its own. function lastTextLine(el) { var last = el.lastElementChild; while (last && /^(DIV|DL|DD|UL|OL|LI)$/.test(last.tagName)) { el = last; last = el.lastElementChild; } return el; } // A pencil on every example and quotation under a sense, opening add-examples' form on that one line. function attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3) { // A queued change redraws the sense (dsb-panels.js raises this); its examples get their pencils again. if (!$li.data("dsbExampleRedraw")) { $li.data("dsbExampleRedraw", true); $li.on("dsbSenseRedrawn", function () { attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3); }); } sections.exampleItemsOf($li).each(function () { var item = this; if ($(item).hasClass("dsb-has-example-toolbar")) { return; } var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Nimûneyê biguhêre"); var $toolbar = $("<span class='dsb-example-toolbar'></span>").append($toggle); $toggle.on("click", function (e) { e.preventDefault(); e.stopPropagation(); withPanels(function (panels) { panels.editExample($li, $h3, language, sectionType, sectionOrdinal, definitionLevel, item); }); }); // Touch has no hover: a tap on the example shows its pencil, as a tap on a sense does. $(lastTextLine(item)).append($toolbar); $(item).addClass("dsb-has-example-toolbar").on("click.dsbReveal", function () { $toolbar.addClass("dsb-revealed"); }); }); } // How many rows the pencil is currently holding open. A pinned row means somebody is part-way through // an edit, and opening another one under the pointer moves the page: the content below shifts, the // pointer ends up over something else, that fires its own hover, and the page jumps back and forth. // So while anything is pinned, hovering opens nothing. var pinnedRows = 0; var canHover = !window.matchMedia || window.matchMedia("(hover: hover)").matches; function attachToolbar($li, language, sectionType, sectionOrdinal, definitionLevel, $h3, isLastSense) { if ($li.children(".dsb-toolbar").length) { return; } var $toolbar = $("<span class='dsb-toolbar'></span>"); // The real InterlanguageLinks pencil icon (same asset defEditButton uses); image swap is in the CSS. var $toggle = $("<a href='#' class='dsb-toggle' title='Guhartina pêşketî'></a>"); // Revealed by $toggle -- an indented panel under the sense, not always visible. Built lazily on // first open rather than eagerly for every sense on the page. var $quickRow = $("<div class='dsb-quick-row'></div>").hide(); var pinned = false; // the pencil pins the row open; hovering alone does not var hovering = false; // Builds the row -- fetching the panels module the first time -- then calls back. Asynchronous now, // so the callback re-checks that the row is still wanted rather than showing it unconditionally. function ensureBuilt(then) { if ($quickRow.data("built")) { then(); return; } withPanels(function (panels) { if (!$quickRow.data("built")) { panels.buildAdvancedPanel($quickRow, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense); $quickRow.data("built", true); } then(); }); } // Anything the user has started here: an open field box, the open dropdown, or focus inside the row. function engaged() { return pinned || $quickRow.find(".dsb-open-boxes").children().length > 0 || $quickRow.find(".dsb-more-menu").is(":visible") || $.contains($quickRow.get(0), document.activeElement); } function setPinned(next) { if (pinned === next) { return; } pinned = next; pinnedRows += next ? 1 : -1; } $toggle.on("click", function (e) { e.preventDefault(); // Through their own cancel handler rather than a bare hide(), so a row this closes also drops // its pinned state -- hiding it alone would leave pinnedRows counting a row nobody can see. $(".dsb-quick-row").not($quickRow).trigger("dsbCancel"); setPinned(!pinned); if (pinned) { ensureBuilt(function () { if (pinned) { $quickRow.show(); } }); } else { $quickRow.hide(); } }); // Hover reveals the row and takes it away again, the way en.wikt's does -- but never while something // on it is in use, or a half-typed box would vanish on the way to the mouse leaving. $li.on("mouseenter.dsbHover", function () { // iOS plays a tap as mouseenter first, and a row shown here would swallow that tap's click. if (!canHover) { return; } hovering = true; // A row the pencil is holding open is already open, and no row opens on hover while another // one is: that is the case where the page is being edited and must stay still. if (pinned || pinnedRows) { return; } // Hover reveals a row that already exists; it never fetches the panels module. Otherwise // simply moving the pointer across an entry would pull ~100 KB on every page view, which is // the whole cost the split exists to avoid. The pencil is what asks for it. if ($quickRow.data("built")) { $quickRow.show(); } }); // Raised by the panel's "Betal bike" (see dsb-panels.js): clear the pinned state as well as // hiding, so one click on the pencil opens it again. $quickRow.on("dsbCancel", function () { setPinned(false); hovering = false; $quickRow.hide(); }); $li.on("mouseleave.dsbHover", function () { hovering = false; if (!engaged()) { $quickRow.hide(); } }); $toolbar.append($toggle); // The class positions the <li>, so the toolbar's gutter slot is measured from the line itself and // not from wherever it happens to sit in the child order -- ShowIDs prepends its id pill to this // same <li>. $li.addClass("dsb-def").prepend($toolbar); // Touch has no :hover -- a tap anywhere on the line reveals the pencil the same way hovering does on // desktop (CSS gates this to touch devices; harmless if it also fires on a mouse click). $li.on("click.dsbReveal", function () { $toolbar.addClass("dsb-revealed"); }); // Land $quickRow right after the sense's own gloss text -- before any existing "Hevmane: ..." <dl>, // citation <ul>, or the Nimûne/jêgirtin proxy link's own <ul>, not appended after all of that. var $firstBlock = $li.children("dl, ul, ol").first(); if ($firstBlock.length) { $firstBlock.before($quickRow); } else { $li.append($quickRow); } } // A section the data marks hidden is left alone. function isHiddenSection(header) { return PAGE_SUBSECTIONS.some(function (s) { return s.hidden && s.header === header; }); } // The translation section, whose pencil opens the translation adder on its boxes. function isWergerSection(header) { return PAGE_SUBSECTIONS.some(function (s) { return s.kind === "werger" && s.header === header; }); } // Everything between a heading and the next heading at its level or above. function sectionBody($heading) { var depth = headingDepth($heading); var $body = $(); for (var $cursor = $heading.next(); $cursor.length; $cursor = $cursor.next()) { var d = headingDepth($cursor); if (d && d <= depth) { break; } $body = $body.add($cursor); } return $body; } // An editor for the translation adder that fetches the panels only when a change is first made, so a // page view with translation boxes does not pull them. function lazyTranslationEditor($h3, language, sectionType) { var real = null; function withEditor(fn) { withPanels(function (panels) { real = real || panels.translationEditor($h3, language, sectionType); panels.openQueueBox(); fn(real); }); } return { withCurrentText: function (callback) { withEditor(function (ed) { ed.withCurrentText(callback); }); }, addEdit: function (edit, node) { withEditor(function (ed) { ed.addEdit(edit, node); }); }, error: function (message) { mw.notify(message, { type: "error" }); }, undo: function () { withEditor(function (ed) { ed.undo(); }); } }; } // Under "==== Werger ====" every box gets the form for a new translation and a ± on its heading, from // [[MediaWiki:Gadget-wikt.translation-adder.js]]. The heading's pencil puts ✎ and × on the existing // lines. All their changes join the save queue. function attachWergerEditor($hh, $heading, $h3, header, language, sectionType) { var $body = sectionBody($heading); if (!$body.find("table.translations").length) { return; } var editor = lazyTranslationEditor($h3, language, sectionType); var adder = mw.loader.using("ext.gadget.wikt.translation-adder").then(function (require) { var api = require("ext.gadget.wikt.translation-adder"); return api.attach($body, editor).then(function () { return api; }); }); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Tercimeyên heyî biguhêre"); $toolbar.append($toggle); placeHeadingToolbar($hh, $toolbar); revealHeadingToolbarOnTap($hh, $toolbar); $toggle.on("click", function (e) { e.preventDefault(); // Adding a translation turns editing on by itself, so the state is read from the page. var open = !$body.find(".translation-adder-term-edit:visible").length; $toolbar.toggleClass("dsb-werger-open", open).toggleClass("dsb-revealed", open); if (open) { // Collapsed boxes are opened, so their lines are in sight. $body.find(".NavFrame").each(function () { if ($(this).children(".NavContent").is(":hidden")) { $(this).find(".NavToggle").first().trigger("click"); } }); } adder.then(function (api) { api.setLineEditing($body, editor, open); }, function () { mw.notify("Nikare amûra tercimeyan bar bike.", { type: "error" }); }); }); } // Every subsection under a part of speech, whether or not the data knows the header: one it does not // know opens as wikitext, or as a word list when that is what it holds -- see buildSubsectionPanel. function attachSubsectionEditors($h3, language, sectionType, sectionOrdinal, posDepth) { posDepth = posDepth || 3; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var depth = headingDepth($cursor); if (depth && depth <= posDepth) { break; } if (depth !== posDepth + 1) { continue; } var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); var header = headingText($hh); if (!header || $hh.data("dsbAttached")) { continue; } if (isWergerSection(header)) { $hh.data("dsbAttached", true); attachWergerEditor($hh, $cursor, $h3, header, language, sectionType); continue; } if (isHiddenSection(header)) { continue; } $hh.data("dsbAttached", true); attachSubsectionEditor($hh, $cursor, $h3, header, language, sectionType, sectionOrdinal); } } // Where the pencil goes depends on what the skin does with its own edit links. Desktop skins put // "[biguhêre]" right after the heading text, so the pencil follows it and the two read as one group. // Minerva parks its edit icon at the far right of the heading row instead, a screen-width away from the // text, so there the pencil goes inside the heading rather than chasing it. function placeHeadingToolbar($heading, $toolbar) { if (mw.config.get("skin") !== "minerva") { var $editsection = headingOuter($heading).find(".mw-editsection").first(); if ($editsection.length) { $editsection.after($toolbar); return; } } $heading.append($toolbar); } // Touch has no hover, so a heading's pencil is shown by tapping the heading itself. Taps that mean // something else are left alone: the skin's own edit links, and the pencil once it is showing. function revealHeadingToolbarOnTap($heading, $toolbar) { headingOuter($heading).on("click", function (e) { if ($(e.target).closest("a, .dsb-toolbar").length) { return; } $toolbar.toggleClass("dsb-revealed"); }); } // One pencil's worth of behaviour, shared by the "====" headings and by an image's own caption: the // panel is built the first time it is opened, never before, and the pencil stays visible while it is. // free, when given, pins the panel to a heading located by its own text rather than through a part of // speech: {header, ordinal}. function bindPanelToggle($toggle, $toolbar, $panel, $h3, header, language, sectionType, sectionOrdinal, free) { $toggle.on("click", function (e) { e.preventDefault(); $panel.toggle(); // Stays put while its panel is open, so it does not vanish from under the pointer. $toolbar.toggleClass("dsb-revealed", $panel.is(":visible")); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal, header, free); }); } revealQueue(); }); } function attachSubsectionEditor($hh, $heading, $h3, header, language, sectionType, sectionOrdinal) { var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " biguhêre"); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($hh, $toolbar); revealHeadingToolbarOnTap($hh, $toolbar); $heading.after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h3, header, language, sectionType, sectionOrdinal); } // The "place" subsection that owns the thumbnails under a part-of-speech heading -- named in the data, // not spelled out here, so renaming it there renames it everywhere. var IMAGE_HEADER = (function () { var found = null; PAGE_SUBSECTIONS.forEach(function (s) { if (s.place === "pos-top") { found = s.header; } }); return found; }()); // A pencil on the image's own caption. A wrong caption is noticed while looking at the picture, and // this is where the writer reaches for it -- the same editor the section's own list offers, opened on // the image that is already there. // Every thumbnail between the heading and the first subsection, which is the run "pos-top" covers. // Anything else in that run is stepped over: a headword line, a declension table or the stylesheet a // template brings with it all sit before the pictures on plenty of entries. function attachImageEditors($h3, language, sectionType, sectionOrdinal) { if (!IMAGE_HEADER) { return; } for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if (headingDepth($cursor)) { break; } if ($cursor.is(".thumb, figure, .floatright, .floatleft")) { attachImageEditor($cursor, $h3, language, sectionType, sectionOrdinal); } } } // A "===" section that is not a part of speech but is one of ours: Bilêvkirin, which sits above the // part-of-speech blocks and belongs to the whole language. function placeSubsectionFor($h3) { var text = headingText($h3); var found = null; PAGE_SUBSECTIONS.forEach(function (s) { if (s.place === "pron" && s.header === text) { found = s; } }); return found; } // The first part-of-speech heading under the same language. The writer works from a section, so // without one there is nothing to anchor the box to. function firstPosHeadingAfter($h3) { for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is(".mw-heading2, h2")) { return null; // the next language starts } var depth = headingDepth($cursor); if (depth !== 3 && depth !== 4) { continue; } var $hh = $cursor.is("h3, h4") ? $cursor : $cursor.find("h3, h4").first(); if (sectionTypeOf($hh) || isManeHeading($hh)) { return $hh; } } return null; } // Its own pencil, opening the same box the section list offers -- a heading nobody can edit from the // page is a heading people edit by hand. function attachPlaceHeadingEditor($h3, subsection) { if ($h3.data("dsbAttached")) { return; } var $pos = firstPosHeadingAfter($h3); if (!$pos) { return; } var $ol = null; for (var $cursor = headingOuter($pos).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("ol")) { $ol = $cursor; break; } if ($cursor.is(".mw-heading2, .mw-heading3, h2, h3")) { break; } } var language = $ol && languageCodeForOl($ol); if (!language) { return; } $h3.data("dsbAttached", true); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", subsection.header + " " + msg.edit); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($h3, $toolbar); revealHeadingToolbarOnTap($h3, $toolbar); headingOuter($h3).after($panel); bindPanelToggle($toggle, $toolbar, $panel, $pos, subsection.header, language, sectionTypeOf($pos) || MANE_SECTION, sectionOrdinalWithinLanguage($pos)); } // Every other heading inside a language: the ones that sit beside the parts of speech rather than // under one ("=== Etîmolojî ===", "=== Çavkanî ==="), and the ones nobody planned for. Each opens // the wikitext under its own heading, or the {{kol3}} form when that is what it holds. function attachFreeSectionEditors() { $("#mw-content-text").find("h3, h4, h5, h6").each(function () { var $h = $(this); // A heading that already carries one of this gadget's pencils is spoken for. if ($h.data("dsbAttached") || headingOuter($h).find(".dsb-toolbar").length) { return; } var header = headingText($h); if (!header || isHiddenSection(header)) { return; } var language = languageCodeForOl($h); if (!language) { return; } $h.data("dsbAttached", true); attachFreeSectionEditor($h, header, language); }); } function attachFreeSectionEditor($h, header, language) { var free = { header: header, ordinal: headingTextOrdinalWithinLanguage($h) }; var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", header + " " + msg.edit); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $toolbar.append($toggle); placeHeadingToolbar($h, $toolbar); revealHeadingToolbarOnTap($h, $toolbar); headingOuter($h).after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h, header, language, null, 0, free); } function attachImageEditor($fig, $h3, language, sectionType, sectionOrdinal) { if ($fig.data("dsbAttached")) { return; } var $caption = $fig.find("figcaption, .thumbcaption").first(); if (!$caption.length) { return; } $fig.data("dsbAttached", true); var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-caption'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", IMAGE_HEADER + " biguhêre"); // After the figure, not inside it: the panel is full width, and a floated thumbnail is not. var $panel = $("<div class='dsb-subsection-panel dsb-panel-after-image'></div>").hide(); $toolbar.append($toggle); $caption.append($toolbar); revealHeadingToolbarOnTap($caption, $toolbar); $fig.after($panel); bindPanelToggle($toggle, $toolbar, $panel, $h3, IMAGE_HEADER, language, sectionType, sectionOrdinal); } function attachSubsectionTrigger($h3, $ol, language, sectionType, sectionOrdinal) { if ($h3.data("dsbAttached")) { return; } $h3.data("dsbAttached", true); var $anchor = $ol; for (var $cursor = $ol.next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is(".mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6, h2, h3, h4, h5, h6")) { break; } $anchor = $cursor; } var $trigger = $("<a href='#' class='dsb-add-subsection'>+ Beşek lê zêde bike</a>"); var $panel = $("<div class='dsb-subsection-panel'></div>").hide(); $trigger.on("click", function (e) { e.preventDefault(); $panel.toggle(); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal); }); } revealQueue(); }); $anchor.after($panel); $anchor.after($trigger); } // Attaches the "choose a part of speech" trigger under a heading whose part of speech is unsettled: an // unclassified "=== Mane ===" placeholder, or a "=== Biwêj ===" that is really a noun, verb, ... . function attachConvertPosTrigger($h3, sectionType) { if ($h3.data("dsbConvertAttached")) { return; } $h3.data("dsbConvertAttached", true); // languageCodeForOl() is really just "nearest preceding .besazimani[id] before this element in // document order" -- nothing ol-specific about it, so it works unchanged for a heading too. var language = languageCodeForOl($h3); if (!language) { return; } var sectionOrdinal = sectionOrdinalWithinLanguage($h3); // The same pencil the "====" subsections get, so reclassifying reads as an edit affordance rather // than a line of body text. var $toolbar = $("<span class='dsb-toolbar dsb-toolbar-heading'></span>"); var $toggle = $("<a href='#' class='dsb-toggle'></a>").attr("title", "Cureyê rêzimanî hilbijêre"); var $panel = $("<div class='dsb-convert-pos-panel'></div>").hide(); $toolbar.append($toggle); $toggle.on("click", function (e) { e.preventDefault(); $panel.toggle(); // Stays put while its panel is open, so it does not vanish from under the pointer. $toolbar.toggleClass("dsb-revealed", $panel.is(":visible")); if (!$panel.is(":visible")) { return; } if (!$panel.data("built")) { $panel.data("built", true); $panel.text(msg.loading); withPanels(function (panels) { $panel.empty(); panels.buildConvertPosPanel($panel, $h3, language, sectionType, sectionOrdinal); }); } revealQueue(); }); // Raised by the panel's "Betal bike": closes it and drops what was typed, so the pencil reopens it fresh. $panel.on("dsbCancel", function () { $panel.hide().empty().data("built", false); $toolbar.removeClass("dsb-revealed"); }); placeHeadingToolbar($h3, $toolbar); revealHeadingToolbarOnTap($h3, $toolbar); headingOuter($h3).after($panel); } // ---- driver ---- function init() { $("h3, h4").each(function () { var $h3 = $(this); var posDepth = headingDepth($h3) || 3; var sectionType = sectionTypeOf($h3); if (!sectionType) { var place = placeSubsectionFor($h3); if (place) { attachPlaceHeadingEditor($h3, place); return; } if (!isManeHeading($h3)) { return; // not a recognized word-sense section (Etîmolojî, Çavkanî, ...) } // An unclassified placeholder: offer the POS picker, then carry on and wire up the same // per-sense pencils, so the POS choice and the sense edits can be queued and saved together. sectionType = MANE_SECTION; attachConvertPosTrigger($h3, sectionType); } else { // Every part of speech, not just the ones most often mistaken: any section can turn out to // be another, and the picker is what changes it. attachConvertPosTrigger($h3, sectionType); } var sectionOrdinal = sectionOrdinalWithinLanguage($h3); var $ol = null; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("ol")) { $ol = $cursor; break; } if (headingDepth($cursor)) { break; } } if (!$ol) { return; } var language = languageCodeForOl($ol); if (!language) { return; } var lastTopIndex = $ol.children("li").not(".mw-empty-elt").length - 1; eachLeafDefinition($ol, function ($li) { var definitionLevel = []; var $definitionItem = $li; while ($definitionItem.prop("tagName") === "LI") { definitionLevel.splice(0, 0, $definitionItem.prevAll("li:not(.mw-empty-elt)").length); $definitionItem = $definitionItem.parent().parent(); } definitionLevel.splice(0, 0, sectionType + "|" + sectionOrdinal); var isLastSense = definitionLevel.length === 2 && definitionLevel[1] === lastTopIndex; attachToolbar($li, language, sectionType, sectionOrdinal, definitionLevel, $h3, isLastSense); attachExampleEditors($li, language, sectionType, sectionOrdinal, definitionLevel, $h3); }); attachSubsectionTrigger($h3, $ol, language, sectionType, sectionOrdinal, posDepth); attachSubsectionEditors($h3, language, sectionType, sectionOrdinal, posDepth); attachImageEditors($h3, language, sectionType, sectionOrdinal); }); // Last, so a heading one of the walks above claimed keeps the pencil that knows more about it. attachFreeSectionEditors(); } // One listener for every "Hevmane û Bêhtir" dropdown on the page. $(document).on("click.dsbMoreMenu", function (e) { $(".dsb-more-menu:visible").each(function () { var $menu = $(this); if ($(e.target).closest($menu).length || $(e.target).closest($menu.prev(".dsb-more-toggle")).length) { return; } $menu.slideUp(); var $arrow = $menu.data("dsbArrow"); if ($arrow) { $arrow.text("▸"); } }); }); // Closes every open panel/preview this gadget can leave in the DOM, so bfcache never freezes one mid-open. function closeAllOpenUi() { if (panelsApi) { panelsApi.clearQueue(); panelsApi.closeAllPanels(); return; } // Before the panels arrive there is nothing built to tear down, only the view-path rows. $(".dsb-quick-row, .dsb-more-menu, .dsb-preview-inline, .dsb-preview-block").hide(); } $(window).on("pagehide", closeAllOpenUi); // Belt-and-suspenders: also close on restore-from-bfcache, in case pagehide's own hide didn't stick. $(window).on("pageshow", function (e) { if (e.originalEvent && e.originalEvent.persisted) { closeAllOpenUi(); } }); $(init); // </nowiki> }()); // </nowiki> 58r0rwwkz31y3161x10u75cc2qhr2t3 Bikarhêner:Wikihez/Gadget-test/dsb-panels.js 2 1358909 6794036 6609069 2026-10-14T19:20:47Z Wikihez 7344 DefSideBoxes: Werger pencil opens the translation adder, whose changes join the save queue 6794036 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.dsb-panels.js]] * * Everything DefSideBoxes shows once you click: the per-sense advanced panel, the subsection editors, * the part-of-speech picker, the template dialogs, the preview and the queued save. * * Loaded on demand by wikt.dsb, never on a plain page view. It exports only what that half calls. */ (function () { "use strict"; var sections = require("ext.gadget.wikt.core").sections; var edit = require("ext.gadget.wikt.edit"); var cite = require("ext.gadget.wikt.cite"); var util = edit.util; var posparams = edit.posparams; var wt = edit.wt; var startData = edit.data; var langs = edit.langs; var escapeRegExp = util.escapeRegExp; // Local names for the shared wikitext layer, so every call site below reads as it always did. var langHeaderSource = wt.langHeaderSource; var canonicalTemplateKey = wt.canonicalTemplateKey; var aliasPattern = wt.aliasPattern; var splitLeadingCalls = wt.splitLeadingCalls; var splitTemplateParams = wt.splitTemplateParams; var parseTemplateCalls = wt.parseTemplateCalls; var splitCallParams = wt.splitCallParams; var buildTemplateCall = wt.buildTemplateCall; var tdText = wt.tdText; var fetchTemplateData = wt.fetchTemplateData; var parseWordListCall = wt.parseWordListCall; var parseWordListCalls = wt.parseWordListCalls; var namedParam = wt.namedParam; var withNamedParam = wt.withNamedParam; var buildWordListCall = wt.buildWordListCall; var parseBulletWordList = wt.parseBulletWordList; var sectionNames = sections.sectionNames; var MANE_SECTION = sections.MANE_SECTION; var PAGE_SUBSECTIONS = sections.PAGE_SUBSECTIONS; var headingOuter = sections.headingOuter; var headingText = sections.headingText; var isManeHeading = sections.isManeHeading; var sectionTypeOf = sections.sectionTypeOf; var sectionOrdinalWithinLanguage = sections.sectionOrdinalWithinLanguage; var isFormOfWrapper = sections.isFormOfWrapper; var eachLeafDefinition = sections.eachLeafDefinition; var api = new mw.Api(); // metadata[lang].g, minus the grammatical-number codes start.js filters out with isNumberCode(), rendered // the way its genderLabel() does ("m-p" -> "mê (pirjimar)"). // start.js asks transitivity and structural form as two questions and joins them as "<form>-<base>", so // "gerguhêz" alone is "gh" and "xwerû" + "gerguhêz" is "x-gh". // Template call convertSectionToPos() drops under a newly-chosen POS heading, e.g. "Hoker" -> "{{hoker|ku}}". // A "Formeke X" (form-of) heading uses the unchanged base pos template. Doubles as the list of template // calls a conversion may find under the OLD heading and replace, rather than leave stacked beside the new one. // A section's template call. "Formeke Xê" uses X's own template with form=erê. Biwêj is recognised so a // conversion can replace its {{biwêj}}, but never written: no new ones. function posTemplateFor(header) { var form = /^Formeke (.+)ê$/.exec(header || ""); if (form && startData.posChoices[form[1]] && sections.hasFormHeading(form[1])) { return { name: form[1], params: ["form=erê"] }; } var key = util.decapitalize(header || ""); return startData.posChoices[key] ? { name: key, params: [] } : null; } // convertSectionToPos() drops whichever of the tag's names is actually present. var MANE_TAG_REGEX = wt.MANE_TAG_REGEX; // The six per-sense groups start.js writes as "#: {{tpl|lang|word1|word2}}" sub-lines under a "#" definition. // Array order controls where a new group line lands relative to existing ones. Every group lives behind the // quick row's "Hevmane û Bêhtir" dropdown; only the gloss and Nimûne/jêgirtin links sit in the row itself. var SENSE_GROUPS = startData.senseGroups; var GLOSS_LABEL = "Maneyê biguhêre"; var NEWLINE = String.fromCharCode(10); // The column word-list template new sections are written with; the rest of wordlistTemplates are // spellings the parser also accepts. var WORDLIST_TEMPLATE = startData.wordlistTemplates[0]; // ---- {{werger-ser}} / {{werger-bin}}: one translation box ---- // The pair brackets the rows; the translation editor gadget writes the rows themselves onto the // rendered page, so this only ever builds (and reads back) the opening call. // Parameters, from [[Şablon:werger-ser]]: 1 = the sense shown in the box head, 2 = a note in // parentheses after it, id = anchor override (defaults to 1), stûn = column count, firehî = column // width (default 26em), bgcolor, valign (default top), align (default left). var WERGER_NAMED = ["id", "stûn", "firehî", "bgcolor", "valign", "align"]; function buildWergerBlock(params) { var parts = ["werger-ser"]; // Positional slots: 2 can only be reached past 1, so an empty 1 still has to be written. if (params["1"] || params["2"]) { parts.push(params["1"] || ""); } if (params["2"]) { parts.push(params["2"]); } WERGER_NAMED.forEach(function (key) { if (params[key]) { parts.push(key + "=" + params[key]); } }); return "{{" + parts.join("|") + "}}\n{{werger-bin}}"; } // The opening call of the first block in `text`, as the same {1, 2, id, ...} object. Empty object when // there is none, so a half-typed draft restores as an empty form rather than throwing. function parseWergerBlock(text) { var out = {}; var body = wergerSerBody(String(text || "")); if (!body) { return out; } var positional = 0; splitTemplateArgs(body).forEach(function (arg) { var eq = arg.indexOf("="); var name = eq === -1 ? "" : $.trim(arg.slice(0, eq)); if (eq !== -1 && WERGER_NAMED.indexOf(name) !== -1) { out[name] = $.trim(arg.slice(eq + 1)); } else { out[String(++positional)] = $.trim(arg); } }); return out; } // Everything after "{{werger-ser|" up to the "}}" that closes it, or "" when there is no call or it // takes no parameters. Scans for the matching close rather than the first one: a gloss can hold a // template of its own, and a lazy regex would stop inside it. function wergerSerBody(text) { var m = /\{\{\s*werger-ser\s*(?=[|}])/.exec(text); if (!m) { return ""; } var depth = 1; var start = m.index + m[0].length; for (var i = start; i < text.length - 1; i++) { var two = text.substr(i, 2); if (two === "{{") { depth++; i++; } else if (two === "}}") { depth--; i++; if (!depth) { return text.charAt(start) === "|" ? text.slice(start + 1, i - 1) : ""; } } } return ""; } // Splits on "|" at brace/bracket depth 0, so a value containing a template or a link stays whole. function splitTemplateArgs(body) { var args = []; var depth = 0; var current = ""; for (var i = 0; i < body.length; i++) { var two = body.substr(i, 2); if (two === "{{" || two === "[[") { depth++; current += two; i++; continue; } if (two === "}}" || two === "]]") { depth--; current += two; i++; continue; } if (body[i] === "|" && depth <= 0) { args.push(current); current = ""; continue; } current += body[i]; } args.push(current); return args; } // Canonical ku.wiktionary "====" subsection order, used only as insertPageSubsection()'s insertion rank // (see subsectionRankForHeader()). Wider than PAGE_SUBSECTIONS: it also covers subsections the gadget // cannot create but still has to order around. var SUBSECTION_ORDER = startData.subsectionOrder; // ---- searchable <select> ---------------------------------------------------------------------------- // Same widget start.js gets from its util.js wrapNativeSelect(): the native <select> stays as the value // holder (so .val() and "change" handlers keep working) but is hidden behind a text input that filters an // overlay list. Ported rather than required() because util.js is a file inside start.js's package module, // which another gadget cannot reach; the template-row decoration start.js uses is left out. // util.js's wrapNativeSelect(): the native <select> stays the value holder behind a filtering text input. // It reads its own placeholder off data-placeholder, and its wiktku-* styles ship in start.css, so both // gadgets get the identical widget instead of two lookalikes. function makeSearchable($select, placeholder) { $select.attr("data-placeholder", placeholder || "Lê bigere…"); return util.wrapNativeSelect($select); } // ---- small shared utilities ---- // The readable half of an mw.Api rejection. var apiErrorMessage = util.apiErrorMessage; // Thrown from inside an edit() transform to bail out with a specific message. var SubmitAbort = util.SubmitAbort; // A gloss is one field on the page: the sense, then {{îzah|…}} explaining it when there is one. function joinGloss(mane, izah) { return izah ? $.trim(mane + " {{îzah|" + izah + "}}") : $.trim(mane); } // The two halves are linked separately, never joined first: linkify leaves template calls alone by // design, so anything inside {{îzah|…}} would never be looked at. // // A half is skipped when there is nothing in it worth looking up, and -- when onlyUnlinked is set -- // when it already carries a link. Someone who has linked a word by hand has made that half theirs, and // a save must not go on linking the rest of it behind their back; "Oto-lînk bike" leaves the flag off // and still links on demand. function linkifyGlossParts(mane, izah, done, onlyUnlinked) { function needsLinking(text) { if (!$.trim(text) || !util.hasLinkableText(text)) { return false; } return !(onlyUnlinked && text.indexOf("[[") !== -1); } function step(text, next) { if (!needsLinking(text)) { next(text, null); return; } util.linkifyText(text, function (linked, error) { // An empty answer would wipe what the user typed, so only real text is taken. next(typeof linked === "string" && $.trim(linked) ? $.trim(linked) : text, error || null); }); } step(mane, function (linkedMane, maneError) { step(izah, function (linkedIzah, izahError) { done(linkedMane, linkedIzah, maneError || izahError); }); }); } // Runs transformFn(content) inside mw.Api().edit()'s own transform callback so one fetch covers both the // edit-conflict check and the content, preventing a stale overwrite of someone else's edit. // // No badtoken retry here on purpose: edit() posts through postWithEditToken -> postWithToken, which // already throws the cached token away and repeats the request once when the wiki says "badtoken". // A second retry of our own would only double it. function editPage(summary, transformFn, onSuccess, onError) { api.edit(mw.config.get("wgPageName"), function (revision) { return { text: transformFn(revision.content), summary: summary }; }).then(onSuccess, function (codeOrAbort, result) { if (codeOrAbort instanceof SubmitAbort) { onError(codeOrAbort.message); } else { onError(util.msg.saveFailed + apiErrorMessage(codeOrAbort, result)); } }); } // ---- rendered preview: wikitext through the parse API, so a preview shows what the templates // actually produce rather than a plain list standing in for them ---- // Same text is usually asked for twice (open the preview, close it, open it again), so the promise is // kept rather than the call repeated. var renderCache = {}; /** * Renders wikitext as this page would render it. Resolves with an HTML string. * * pst: 1 applies pre-save transform, so ~~~~ and subst: behave as they will once saved. The * modulestyles/modules the parse reports are loaded before resolving: without them TemplateStyles is * missing and the preview would show the right markup in the wrong clothes. */ function renderWikitext(wikitext) { if (renderCache[wikitext]) { return renderCache[wikitext]; } renderCache[wikitext] = api.post({ action: "parse", format: "json", formatversion: 2, title: mw.config.get("wgPageName"), text: wikitext, contentmodel: "wikitext", prop: "text|modules|jsconfigvars", pst: 1, disablelimitreport: 1, disableeditsection: 1, disabletoc: 1 }).then(function (data) { var parsed = (data && data.parse) || {}; if (parsed.modulestyles && parsed.modulestyles.length) { mw.loader.load(parsed.modulestyles); } if (parsed.modules && parsed.modules.length) { mw.loader.load(parsed.modules); } return parsed.text || ""; }, function () { // A failed render must not be remembered, or the next attempt gets the rejection back. delete renderCache[wikitext]; return $.Deferred().reject().promise(); }); return renderCache[wikitext]; } /** * Puts rendered wikitext into $target, replacing whatever placeholder is there. * * The site's own gadgets are told about it through the "wikipage.content" hook, the same way * MediaWiki announces real content, so collapsible lists and the like behave in the preview too. */ // MediaWiki warns when the hook is fired on content that is not in the document, and a gadget that // listens to it can do nothing useful with nodes nobody can see. function announceContent($el) { if ($el.closest("html").length) { mw.hook("wikipage.content").fire($el); } } function fillWithRendered($target, wikitext, onFail) { return renderWikitext(wikitext).then(function (html) { if (!$target.closest("body").length) { return; // the preview was dropped while the parse was in flight } $target.empty().append($("<div class='mw-parser-output'></div>").html(html)); announceContent($target); }, function () { if (onFail) onFail(); }); } /** * Puts a rendered sense in place of the one on the page, keeping this gadget's own furniture (the * pencil's toolbar, the quick row, any open panel) where it is. The page's own nodes are detached * rather than dropped, so the returned function can put them straight back when the change is * cancelled or superseded. * * Nothing labels the result: the change is shown by the sense simply reading the way it will read, * and the queue box is what says nothing is saved yet. */ function replaceSenseInPlace($li, item) { var html = item.html; var $anchor = $li.children(".dsb-toolbar").last(); // class/id/data-* off the parsed <li>, so a queued maneid shows its pill here exactly as it will // once saved. The old values are kept for the restore below. var node = $li.get(0); var before = {}; $.each(node.attributes, function (_, attr) { before[attr.name] = attr.value; }); $.each(item.attrs, function (name, value) { if (name !== "class") { $li.attr(name, value); } }); var addedClasses = (item.attrs["class"] || "").split(/\s+/).filter(function (c) { return c && !$li.hasClass(c); }); $li.addClass(addedClasses.join(" ")); // add-examples hangs its own <ul> off the sense and opens its form as that <ul>'s next sibling. // Detaching those leaves the form to open into a tree nobody can see -- "+ Nimune/jegirtin" takes // the click and shows nothing -- so they stay where they are. Only the rows already in that <ul> // are hidden, since the preview draws the examples itself; the tool's own title line stays. var $keep = $li.children("ul, .add-example-form"); var $hiddenRows = $keep.filter("ul").children("li").filter(function () { return !$(this).find(".add-example-form-title").length; }); $hiddenRows.addClass("dsb-preview-hidden"); var originals = $li.contents().filter(function () { return !$(this).is(".dsb-toolbar, .dsb-quick-row, .dsb-subsection-panel, .dsb-preview-block, .dsb-sense-preview"); }).not($keep).get(); // Where the sense's own nodes sat, so cancelling puts them back there rather than above the quick // row -- the row belongs between the sense and its examples. var backAfter = originals.length ? originals[0].previousSibling : null; var $rendered = $("<span class='dsb-sense-preview'></span>").html(html); if ($anchor.length) { $anchor.after($rendered); } else { $li.prepend($rendered); } $(originals).detach(); announceContent($rendered); // The id pill is drawn by the same code a page load uses, so a queued maneid looks like a saved // one rather than like something this gadget invented. if (window.wikt && wikt.showIDs && wikt.showIDs.process) { wikt.showIDs.process($li); } // dsb.js puts the example pencils back on the redrawn examples. The originals keep theirs. $li.triggerHandler("dsbSenseRedrawn"); return function () { $hiddenRows.removeClass("dsb-preview-hidden"); $li.removeClass(addedClasses.join(" ")); $.each(item.attrs, function (name) { if (name === "class") { return; } if (Object.prototype.hasOwnProperty.call(before, name)) { $li.attr(name, before[name]); } else { $li.removeAttr(name); } }); $li.children(".senseid-displayed, .etymid-displayed, .etymonid-displayed").remove(); if (backAfter && backAfter.parentNode === node) { $(backAfter).after(originals); } else if ($anchor.length) { $anchor.after(originals); } else { $li.prepend(originals); } $rendered.remove(); }; } /** * The <li> out of a parsed "#" block, and the attributes it carries. * * The parse wraps it in its own <ol>, which must not reach the page: the sense already has a number * from the list it lives in. A leading {{maneid}} also makes the parser emit an empty * "li.mw-empty-elt" ahead of the real one, so the first <li> is not necessarily the sense. * * The attributes matter as much as the content: {{maneid|ku|x}} renders as class="senseid" * data-id="x" on the <li> itself, which is what wikt.showids draws its pill from. */ function parsedSenseItem(html, depth) { var $item = $("<div></div>").html(html).find("ol > li").filter(function () { return !$(this).hasClass("mw-empty-elt"); }).first(); // A sub-sense parsed on its own comes back one <ol> deeper per "#": "##" is a list inside a list. // Descend as far as the sense's own depth, or its number would be drawn inside a second list -- // "2." with a "1." under it. for (var level = 1; level < (depth || 1) && $item.length; level++) { var $inner = $item.children("ol").children("li").filter(function () { return !$(this).hasClass("mw-empty-elt"); }).first(); if (!$inner.length) { break; } $item = $inner; } if (!$item.length) { return { html: html, attrs: {} }; } var attrs = {}; $.each($item.get(0).attributes, function (_, attr) { attrs[attr.name] = attr.value; }); return { html: $item.html(), attrs: attrs }; } // This gadget's own save queue + floating widget, separate from the shared Gadget-Editor queue. // Each entry keeps its own transformFn so saveQueueNow() can re-run all of them against one fresh fetch. var dsbQueue = { // [{key, summary, transformFn, runLast, removePreview}]; transformFn: baseContent -> {content, warnings}. // removePreview lives on the entry so replacing one entry takes its own live DOM preview with it. entries: [] }; var $queueBox = null; function dropPreview(entry) { if (typeof entry.removePreview === "function") { try { entry.removePreview(); } catch (e) { /* a preview failing to clean up should never block the queue itself */ } entry.removePreview = null; } } function clearAllPreviews() { dsbQueue.entries.forEach(dropPreview); } function clearQueue() { clearAllPreviews(); dsbQueue.entries = []; if ($queueBox) { $queueBox.$list.empty(); $queueBox.$box.hide(); } } // Closes every editor UI on the page. Each open box's own "x" clears its getter, restores its picker // link and updates the counter, so those are triggered rather than repeating that work here; hiding a // row alone would leave its fields populated and the next pencil click would reopen them half-filled. function closeAllPanels() { $(".dsb-open-boxes .dsb-box-remove").trigger("click"); $(".dsb-quick-note").hide().text(""); $(".dsb-more-menu").hide(); $(".dsb-subsection-panel, .dsb-convert-pos-panel").hide(); // dsb.js owns each pencil's pinned state; tell it rather than hiding behind its back, or the next // click on the pencil would only toggle "pinned" off and appear to do nothing. $(".dsb-quick-row").trigger("dsbCancel"); } function ensureQueueBox() { if ($queueBox) { return $queueBox; } var $box = $("<div class='dsb-queue-box'></div>").hide(); var $close = $("<a href='#' class='dsb-queue-close' title='Veşêre'>&#215;</a>"); var $title = $("<div class='dsb-queue-title'></div>").text("Guhartinên li benda qeydkirinê"); var $list = $("<ul class='dsb-queue-list'></ul>"); var $empty = $("<div class='dsb-queue-empty'></div>").text( "Guhartinên te dê li vir werin rêzkirin. Heta ku tu pêl «Guhartinan qeyd bike» nekî, " + "tiştek li ser rûpelê nayê weşandin." ); var $status = $("<div class='dsb-queue-status'></div>"); var $save = $("<button type='button' class='dsb-queue-save'>Guhartinan qeyd bike</button>"); var $cancel = $("<button type='button' class='dsb-queue-cancel'></button>").text(util.msg.cancel); var $actions = $("<div class='dsb-queue-actions'></div>").append($save, $cancel); $close.on("click", function (e) { e.preventDefault(); $box.hide(); }); $cancel.on("click", function () { clearQueue(); closeAllPanels(); }); $save.on("click", function () { saveQueueNow(); }); $box.append($close, $title, $empty, $list, $actions, $status); $(document.body).append($box); $queueBox = { $box: $box, $list: $list, $empty: $empty, $status: $status, $save: $save, $cancel: $cancel }; return $queueBox; } // Takes one change out of the queue and puts the page back as it was before it, its in-place preview // included. The queue is the only record of what "Guhartinan qeyd bike" will write, so every line in it // has to be removable on its own -- closing the panel that made a change does not unmake it. // Each preview draws the page the whole queue would save, so one entry leaving changes what all the // others should show. They are taken down and drawn again from what is left. function redrawPreviews() { dsbQueue.entries.forEach(function (entry) { if (typeof entry.previewFn !== "function") { return; } dropPreview(entry); try { var removePreview = entry.previewFn(); if (typeof removePreview === "function") { entry.removePreview = removePreview; } } catch (e) { /* a broken preview never stops the queue */ } }); } function dropQueueEntry(entry) { var at = dsbQueue.entries.indexOf(entry); if (at === -1) { return; } dropPreview(entry); dsbQueue.entries.splice(at, 1); redrawPreviews(); renderQueueBox(); hideQueueIfIdle(); } function renderQueueBox() { var box = ensureQueueBox(); box.$list.empty(); dsbQueue.entries.forEach(function (entry) { var $drop = $("<button type='button' class='dsb-queue-drop'>&#215;</button>") .attr("title", "Vê guhartinê ji rêzê derxe"); $drop.on("click", function () { dropQueueEntry(entry); }); box.$list.append($("<li></li>").text(entry.summary).append($drop)); }); box.$empty.toggle(!dsbQueue.entries.length); box.$status.removeClass("dsb-status-ok dsb-status-error").text(""); box.$save.prop("disabled", !dsbQueue.entries.length); box.$box.show(); } // Shown the moment a panel opens, empty, so it is clear before anything is typed that a change waits // for "Guhartinan qeyd bike" rather than publishing itself. function openQueueBox() { renderQueueBox(); } // Nothing queued and no form left open means nothing to save, so the box goes away rather than // lingering on an empty state. function hideQueueIfIdle() { if (dsbQueue.entries.length || !$queueBox) { return; } if ($(".dsb-quick-row:visible, .dsb-subsection-panel:visible, .dsb-convert-pos-panel:visible").length) { return; } $queueBox.$box.hide(); } // Asks core's postEdit module to show its toast on the next load (dsb.js loads it there), then reloads. // Loaded here rather than declared as a dependency, so a reload happens even if it fails. function reloadWithPostEditNotice() { mw.loader.using("mediawiki.action.view.postEdit").then(function (require) { require("mediawiki.action.view.postEdit").fireHookOnPageReload("saved"); }).always(function () { location.reload(); }); } // A transform returns either the new page text or {content, warnings}; both shapes are accepted. // One that returns neither stops the save, named, rather than writing a broken page. function transformedContent(entry, pageContent) { var result = entry.transformFn(pageContent); var next = typeof result === "string" ? result : (result && result.content); if (typeof next !== "string") { throw new SubmitAbort("Guhartin nehate sepandin: " + entry.summary); } return next; } // Queue order, except that runLast entries move to the end: converting "=== Mane ===" into a real POS // heading renames the very heading every other queued change locates itself by. // Those run from the bottom of the page up: a rename changes the ordinals of the sections after it // ("Mane 2" becomes "Mane 1"), never of the ones before. // Two sense paths in page order; index 0 of a definitionLevel is unused. function compareSensePath(a, b) { var len = Math.max(a.length, b.length); for (var i = 1; i < len; i++) { var x = a[i] === undefined ? -1 : a[i]; var y = b[i] === undefined ? -1 : b[i]; if (x !== y) { return x - y; } } return 0; } function orderedEntries() { var normal = []; var last = []; dsbQueue.entries.forEach(function (entry) { (entry.runLast ? last : normal).push(entry); }); last.sort(function (a, b) { return b.pagePosition - a.pagePosition; }); // A sense is found by its number, so removing or adding one moves every sense below it. The sense // changes therefore run bottom-up, each in the slot it was queued in, and every one of them still // finds the line it was queued for. var slots = []; normal.forEach(function (entry, i) { if (entry.sensePath) { slots.push(i); } }); var senses = slots.map(function (i) { return normal[i]; }); senses.sort(function (a, b) { return compareSensePath(b.sensePath, a.sensePath); }); slots.forEach(function (slot, k) { normal[slot] = senses[k]; }); return normal.concat(last); } /** * The pending page as previews and example lookups need it: every queued transform, in save order, * except the runLast section renames. Those callers find their section under the heading it has on * the page now, which a queued "Mane" -> "Navdêr" would take away, and a rename leaves the senses as * they are. An entry that cannot apply is skipped -- the save reports that properly, and a preview * must not shout. */ function queuedContent(pageContent) { var content = pageContent; orderedEntries().filter(function (entry) { return !entry.runLast; }).forEach(function (entry) { try { content = transformedContent(entry, content); } catch (e) { // Left out of the preview; the save reports it properly. Named, so a change that fails to // draw is distinguishable from one with nothing to draw. mw.log.warn("wikt.dsb: pêşdîtin bêyî vê guhartinê hate xêzkirin — " + entry.summary, e); } }); return content; } /** * Where a sense sits in the page the queue would save. A queued removal of an earlier sense moves * everything below it up one, while the path was read off the page before that. Returns null when the * sense itself, or a sense it hangs under, is the one being removed. */ function queuedSensePath(language, definitionLevel) { var path = definitionLevel.slice(); var gone = false; dsbQueue.entries.forEach(function (entry) { var other = entry.removesSense && entry.sensePath; if (!other || entry.senseLanguage !== language || other[0] !== path[0]) { return; } var depth = other.length - 1; if (depth >= path.length) { return; // a sub-sense of something else; it moves nothing here } for (var i = 1; i < depth; i++) { if (other[i] !== path[i]) { return; // another branch of the list } } if (other[depth] < path[depth]) { path[depth] -= 1; } else if (other[depth] === path[depth]) { gone = true; } }); return gone ? null : path; } /** * Redraws one sense where it sits, from the page the queue would save -- so a queued gloss edit, a * queued example and everything else pending on that sense are shown together, rendered by the parser * rather than described in a box. Returns the queue's own "take the preview down" function. * * One preview per sense: a second queued change takes the first drawing down and draws the queue. */ function previewSense($li, language, sectionType, sectionOrdinal, definitionLevel) { var cancelled = false; var restore = null; function undo() { cancelled = true; if (restore) { restore(); restore = null; } if ($li.data("dsbPreview") === undo) { $li.removeData("dsbPreview"); } } var previous = $li.data("dsbPreview"); if (typeof previous === "function") { previous(); } $li.data("dsbPreview", undo); fetchPageContent().then(function (pageContent) { if (cancelled) { return; } var path = queuedSensePath(language, definitionLevel); if (!path) { return; } var wikitext; try { wikitext = senseBlockWikitext(queuedContent(pageContent), language, sectionType, sectionOrdinal, path); } catch (e) { return; } renderWikitext(wikitext).then(function (html) { if (cancelled || $li.data("dsbPreview") !== undo || !$li.closest("body").length) { return; } restore = replaceSenseInPlace($li, parsedSenseItem(html, definitionLevel.length - 1)); }); }); return undo; } // The real save: re-runs every queued transformFn, in order, against one freshly-fetched revision and // posts the result as a single edit with a combined summary. function saveQueueNow() { if (!dsbQueue.entries.length) { return; } var box = ensureQueueBox(); box.$save.prop("disabled", true); box.$cancel.prop("disabled", true); box.$status.removeClass("dsb-status-ok dsb-status-error").text("Tê qeydkirin…"); var entries = orderedEntries(); // Grouped by place, in the order the places were first queued: several changes to one section read // as "Kurmancî/Navdêr 2: a, b, c" rather than repeating the section name in front of each of them. var order = []; var byPlace = {}; entries.forEach(function (entry) { if (!Object.prototype.hasOwnProperty.call(byPlace, entry.place)) { byPlace[entry.place] = []; order.push(entry.place); } byPlace[entry.place].push(entry.action); }); var combinedSummary = order.map(function (place) { var actions = byPlace[place].join(", "); return place ? place + ": " + actions : actions; }).join("; ") + " (DefSideBoxes)"; editPage( combinedSummary, function (pageContent) { var content = pageContent; entries.forEach(function (entry) { content = transformedContent(entry, content); }); return content; }, function (data) { forgetPageContent(); clearAllPreviews(); dsbQueue.entries = []; box.$list.empty(); box.$status .removeClass("dsb-status-error") .addClass("dsb-status-ok") .text("✓ Hemû guhartin hatin qeydkirin."); box.$save.prop("disabled", true); box.$cancel.prop("disabled", false); // A real edit reloads the page and gets MediaWiki's own "Guhartina te hat weşandin." toast, the // one a normal save shows. A null edit changed nothing, so the page stays as it is. if (data && data.newrevid) { reloadWithPostEditNotice(); } }, function (message) { box.$save.prop("disabled", false); box.$cancel.prop("disabled", false); box.$status.removeClass("dsb-status-ok").addClass("dsb-status-error").text(message); } ); } // "Kurmancî/Navdêr" -- where a queued change lands, for its summary. The language name comes from the // shipped list; an unknown code is left as the code itself. // "Kurmancî/Navdêr", or "Kurmancî/Navdêr 2" where the language has more than one section of that // type. Without the number a summary on a page with two Navdêr sections named neither of them. // Numbered only when it disambiguates: on the usual page "Navdêr 1" would just be noise. function placeLabel(language, sectionType, $h3) { return langLabel(language) + "/" + sectionLabel(sectionType, $h3); } // Bilêvkirin sits above the parts of speech and belongs to the whole language, so naming the section // it was opened from would say something untrue about what the edit touches. function subsectionPlaceLabel(subsection, language, sectionType, $h3) { if (subsection && subsection.free) { return langLabel(language) + "/" + subsection.header; } return subsection && subsection.place === "pron" ? langLabel(language) : placeLabel(language, sectionType, $h3); } function sectionLabel(sectionType, $h3) { if ($h3 && $h3.length && sections.sectionTypeCountWithinLanguage($h3) > 1) { return sectionType + " " + sections.sectionOrdinalWithinLanguage($h3); } return sectionType; } // Capitalised because it opens the summary; Kurdish otherwise writes language names lower-case. function langLabel(language) { var names = langs.names(); return util.capitalize(names[language] || language); } // The parts of a sense the form actually changed, so the summary lists them instead of saying nothing. // A save on a sense supersedes the one queued for it (they share a key), so it has to write that one's // fields too. newer over older: a field the newer save touched wins, the rest stays as the older save // left it. A gloss is only ever present when its box was open, so a missing one means untouched. function mergeSenseChanges(older, newer) { if (!older) { return newer; } var merged = { gloss: newer.gloss || older.gloss, maneid: newer.changed.maneid ? newer.maneid : older.maneid, etiket: newer.changed.etiket ? newer.etiket : older.etiket, kategori: newer.changed.kategori ? newer.kategori : older.kategori, words: {}, changed: $.extend({}, older.changed, newer.changed) }; SENSE_GROUPS.forEach(function (group) { merged.words[group.key] = newer.changed["word:" + group.key] ? newer.words[group.key] : (older.words[group.key] || []); }); return merged; } // The sense's fields as the page has them, with a queued change written over the top: what the boxes // prefill from and what collectChanges() diffs against, so reopening a box shows the value that is // waiting to be saved rather than the one it is about to replace. function overlaySenseFields(fields, queued) { if (!queued) { return fields; } var out = { gloss: queued.gloss || fields.gloss, maneid: queued.changed.maneid ? queued.maneid : fields.maneid, etiket: queued.changed.etiket ? queued.etiket : fields.etiket, kategori: queued.changed.kategori ? queued.kategori : fields.kategori, words: {} }; SENSE_GROUPS.forEach(function (group) { out.words[group.key] = queued.changed["word:" + group.key] ? queued.words[group.key] : fields.words[group.key]; }); return out; } function senseChangeLabels(changes) { var parts = []; var changed = changes.changed || {}; if (changes.gloss) { parts.push("mane"); } if (changes.newSense) { parts.push("maneyeke nû"); } SENSE_GROUPS.forEach(function (group) { if (changed["word:" + group.key]) { parts.push(group.label.toLowerCase()); } }); if (changed.maneid) { parts.push("id"); } if (changed.etiket) { parts.push("etîket"); } if (changed.kategori) { parts.push("kategorî"); } return parts; } // Queues transformFn(baseWikitext) -> {content, warnings} for the real save to run later against fresh // content (see saveQueueNow()); nothing here touches the page yet. Also runs previewFn(), which drops a // small "not saved yet" preview into the live DOM at the change's own spot on the page. Shared by every // panel that queues a wikitext change: buildAdvancedPanel, buildSubsectionPanel, buildConvertPosPanel. // place is what the change is being made to ("Kurmancî/Navdêr 2"), action is what is being done to it. // Kept apart rather than pre-joined so saveQueueNow() can name a section once and list everything // queued against it after it, instead of repeating the place before every item. function queueChange(place, action, transformFn, $save, $status, previewFn, opts) { opts = opts || {}; var entry = { key: opts.key || null, place: place || "", action: action, summary: place ? place + ": " + action : action, transformFn: transformFn, runLast: !!opts.runLast, pagePosition: opts.pagePosition || 0, // Which sense the change belongs to, so orderedEntries() can apply the senses bottom-up. sensePath: opts.sensePath || null, // The language section that path is counted in, and whether this change deletes the sense. senseLanguage: opts.senseLanguage || null, removesSense: !!opts.removesSense, // The sense fields this entry writes, merged with whatever an earlier entry under the same // key wrote, so superseding it does not drop what it had already collected. senseChanges: opts.senseChanges || null, // Kept, not just run: redrawPreviews() calls it again when another entry leaves the queue. previewFn: typeof previewFn === "function" ? previewFn : null, removePreview: null }; // Queuing the same target twice replaces the pending change rather than stacking a second one -- // two "+Têkildar" entries would both run, and the second would abort on "Ev beş jixwe heye". var superseded = false; if (entry.key) { for (var i = dsbQueue.entries.length - 1; i >= 0; i--) { if (dsbQueue.entries[i].key === entry.key) { dropPreview(dsbQueue.entries[i]); dsbQueue.entries.splice(i, 1); superseded = true; } } } dsbQueue.entries.push(entry); $status.removeClass("dsb-status-error").addClass("dsb-status-ok").text( "✓ li rêza guhartinan hate zêdekirin — pêl «Guhartinan qeyd bike» bike da ku bi cih bibe." ); $save.prop("disabled", false); if (previewFn) { try { var removePreview = previewFn(); if (typeof removePreview === "function") { entry.removePreview = removePreview; } } catch (e) { /* a broken preview should never stop the change from being queued */ } } // What the superseded entry drew is gone from the queue, so whatever else is drawn from it is stale. // A queued removal renumbers the senses under it, so every other preview is stale the same way. if (superseded || entry.removesSense) { dsbQueue.entries.forEach(function (other) { if (other !== entry) { dropPreview(other); } }); redrawPreviews(); } renderQueueBox(); } // Runs text through the ext.gadget.linkify gadget ($.kuLinkify) and calls back with the linkified result. // Passes the text through unchanged if the gadget can't be loaded, so it never blocks a save. var linkifyText = util.linkifyText; // ---- wikitext location ---- // // One copy for both editing gadgets, in wikt.edit: add-examples writes into the same places. var langSectionRange = wt.langSectionRange; var locateSection = wt.locateSection; var findDefinitionLine = wt.findDefinitionLine; /** * The wikitext of one sense's own block: its "#" line plus everything nested under it -- the "#:" word * lines, the "#*" citations and any sub-senses -- stopping at the next line of the same depth that * starts a new sense. Parsed on its own that block renders as exactly the <li> the page shows. */ function senseBlockWikitext(pageContent, language, sectionType, sectionOrdinal, definitionLevel) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var depth = definitionLevel.length - 1; var out = [lines[defLine]]; for (var i = defLine + 1; i < lines.length; i++) { var m = /^(#+)([:*]?)/.exec(lines[i]); if (!m || m[1].length < depth) { break; } // Same depth and no ":" or "*" after the hashes is the next sense, not part of this one. if (m[1].length === depth && !m[2]) { break; } out.push(lines[i]); } return out.join("\n"); } // Inserts or extends a "#: {{tplKey|lang|word1|word2}}" sub-line for one sense (start.js's wordLine() // format). If a line for this tplKey already exists under the definition, missing words are appended to // it; otherwise a new line lands after the last existing group line, always before any "#*" citation lines. function insertSenseWords(pageContent, language, sectionType, sectionOrdinal, definitionLevel, tplKey, words, opts) { opts = opts || {}; var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = new Array(definitionLevel.length).join("#"); // definitionLevel.length - 1 "#" characters // Group 1 is the spelling the page uses, group 2 the parameters: an existing {{hevwate}} stays // {{hevwate}} rather than being silently renamed to {{hevmane}}. // ".*" rather than "[^}]*": a list holding a nested call ("{{hevmane|ku|{{g|ku|x}}}}") stopped // matching at the first "}", and an unrecognized line meant a second one was written beside it. var tplLineRegex = new RegExp("^" + marker + ":\\s*\\{\\{\\s*(" + aliasPattern(tplKey) + ")\\s*\\|(.*)\\}\\}\\s*$"); var groupKeysPattern = SENSE_GROUPS.map(function (g) { return aliasPattern(g.key); }).join("|"); var groupLineRegex = new RegExp("^" + marker + ":\\s*\\{\\{\\s*(" + groupKeysPattern + ")\\s*\\|"); var anyBlockLineRegex = new RegExp("^" + marker + "[:*]"); var lastGroupLine = -1; for (var i = defLine + 1; i < lines.length; i++) { if (!anyBlockLineRegex.test(lines[i])) { break; } var tplMatch = tplLineRegex.exec(lines[i]); if (tplMatch) { var foundKey = tplMatch[1]; if (opts.replace) { // Editing an existing list: an empty result means the user cleared it, so drop the line. if (words.length) { lines[i] = marker + ": {{" + foundKey + "|" + language + "|" + words.join("|") + "}}"; } else { lines.splice(i, 1); } return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } var params = tplMatch[2].split("|"); // [lang, word1, word2, ...] var existingWords = params.slice(1); words.forEach(function (w) { if (existingWords.indexOf(w) === -1) { existingWords.push(w); } }); lines[i] = marker + ": {{" + foundKey + "|" + language + "|" + existingWords.join("|") + "}}"; return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } if (groupLineRegex.test(lines[i])) { lastGroupLine = i; } } // Nothing to write and no line to edit: a call with no words is a module error ("Parametreya 2 // lazim e"), so an empty list leaves the page alone rather than writing one. if (!words.length) { return pageContent; } var insertAt = lastGroupLine >= 0 ? lastGroupLine + 1 : defLine + 1; lines.splice(insertAt, 0, marker + ": {{" + tplKey + "|" + language + "|" + words.join("|") + "}}"); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Canonical left-to-right order of the small templates that can prefix a definition line's gloss text: // maneid, then kategori (K), then etîket (e). var LEADING_TPL_ORDER = startData.leadingTplOrder; // True for a leading call the advanced panel edits in a box of its own -- those are kept out of the // gloss box so the same template is never editable in two places at once. Redirect spellings count: // {{f}} and {{etîket}} are {{e}}. function hasOwnFieldBox(key) { return LEADING_TPL_ORDER.indexOf(canonicalTemplateKey(key)) !== -1; } // Inserts, extends, or (with opts.replace) overwrites one of the small templates that prefix a definition // line's gloss text -- {{maneid|lang|id}}, {{K|lang|kat1|...}} or {{e|lang|etîket1|...}}. Under // opts.replace an empty values list removes the call rather than writing an empty one. function insertLeadingSenseTpl(pageContent, language, sectionType, sectionOrdinal, definitionLevel, tplKey, values, opts) { opts = opts || {}; var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var afterMarker = lines[defLine].slice(marker.length).replace(/^\s+/, ""); // Collecting the whole leading-call run means an existing tplKey call is found and merged wherever it // already sits, not just when it happens to be first. var split = splitLeadingCalls(afterMarker); var calls = split.calls; var rest = split.rest; var existing = null; calls.forEach(function (c) { if (canonicalTemplateKey(c.key) === tplKey) { existing = c; } }); if (existing) { if (opts.replace) { // Editing an existing call: an empty result means the user cleared it, so drop the call. if (values.length) { existing.params = [language].concat(values); } else { calls = calls.filter(function (c) { return c !== existing; }); } } else { var existingVals = existing.params.slice(1); values.forEach(function (v) { if (existingVals.indexOf(v) === -1) { existingVals.push(v); } }); existing.params = [language].concat(existingVals); } } else if (!values.length) { return pageContent; // nothing to add, and nothing there to clear } else { // Not present yet -- insert right before the first existing call that outranks tplKey in // LEADING_TPL_ORDER (an unrecognized call always outranks everything), or at the end of the run. var myRank = LEADING_TPL_ORDER.indexOf(tplKey); var insertAt = calls.length; for (var i = 0; i < calls.length; i++) { var otherRank = LEADING_TPL_ORDER.indexOf(canonicalTemplateKey(calls[i].key)); if (otherRank === -1 || otherRank > myRank) { insertAt = i; break; } } calls.splice(insertAt, 0, { key: tplKey, params: [language].concat(values) }); } var rebuilt = calls.map(function (c) { return "{{" + c.key + "|" + c.params.join("|") + "}}"; }).join(" "); lines[defLine] = marker + " " + (rebuilt ? rebuilt + " " : "") + rest; return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Replaces just the gloss text of a definition line, leaving any leading template calls untouched -- // the save half of "Maneyê biguhêre" (edit sense). function updateSenseGloss(pageContent, language, sectionType, sectionOrdinal, definitionLevel, newGloss) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var afterMarker = lines[defLine].slice(marker.length).replace(/^\s+/, ""); // The box holds everything except the calls with their own field, so those are put back in front. // Dropping "{{mane?}}" here is what clears start.js's placeholder once a real gloss is written. var kept = splitLeadingCalls(afterMarker).calls.filter(function (c) { return hasOwnFieldBox(c.key); }); var leadingText = kept.map(function (c) { return "{{" + c.key + "|" + c.params.join("|") + "}}"; }).join(" "); lines[defLine] = marker + " " + (leadingText ? leadingText + " " : "") + newGloss; return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Deletes one sense: its definition line plus everything indented under it (its "#:" groups, "#*" // citations, any sub-senses). Refuses to empty a section, since a POS heading with no senses is worse // than the sense the user wanted gone. // A new sense goes directly below the one whose pencil asked for it -- after the lines that sense owns // (its "#:" examples, "#*" citations and "##" sub-senses), so it reads as the next sense rather than // landing at the foot of the section. It takes the same marker depth as its neighbour, so asking from // a "##" sub-sense adds a sibling sub-sense. // extras are the sense's own "#:" lines, written under it in one go: a sense that is not on the // page yet cannot be located by number, so everything it carries goes in with the insertion. function insertNewSense(pageContent, language, sectionType, sectionOrdinal, definitionLevel, text, extras) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var end = defLine + 1; var ownedRegex = new RegExp("^" + marker + "[#:*]"); while (end < lines.length && ownedRegex.test(lines[end])) { end++; } var block = [marker + " " + text]; (extras || []).forEach(function (extra) { block.push(marker + extra); }); lines.splice.apply(lines, [end, 0].concat(block)); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } function removeSense(pageContent, language, sectionType, sectionOrdinal, definitionLevel) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var end = defLine + 1; var ownedRegex = new RegExp("^" + marker + "[#:*]"); while (end < lines.length && ownedRegex.test(lines[end])) { end++; } // Top-level senses of this section only -- "##" sub-senses go with their parent either way. if (definitionLevel.length === 2) { var siblings = 0; for (var i = loc.posLineIndex + 1; i < lines.length && !/^==/.test(lines[i]); i++) { if (/^#[^*#:]/.test(lines[i])) { siblings++; } } if (siblings < 2) { throw new SubmitAbort("Ev maneya dawî ya vê beşê ye. Ji bo rakirinê divê tu tevahiya beşê rakî."); } } lines.splice(defLine, end - defLine); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Fetches the page's current wikitext and pulls out just this sense's gloss text to prefill the "Maneyê // biguhêre" form. A plain read, not part of the hardened save path -- the actual save still re-fetches // fresh content, so only the prefilled text itself could be a little stale. // Current contents of one existing subsection, for prefilling the edit form. // This page's current wikitext. // One fetch for the whole queue: every preview reads the same revision, and asking per preview meant // downloading the page again for each queued change. Dropped after a save, when the page has moved on. var pageContentPromise = null; function forgetPageContent() { pageContentPromise = null; } function fetchPageContent() { if (pageContentPromise) { return pageContentPromise; } pageContentPromise = rawPageContent(); // A failed read must not be remembered as the answer. pageContentPromise.fail(forgetPageContent); return pageContentPromise; } function rawPageContent() { return api.get({ action: "query", prop: "revisions", rvprop: "content", titles: mw.config.get("wgPageName"), formatversion: 2 }).then(function (data) { var page = data.query && data.query.pages && data.query.pages[0]; var content = page && !page.missing && page.revisions && page.revisions[0] && page.revisions[0].content; if (!content) { throw new SubmitAbort("Nikare muhtewaya rûpelê bixwîne."); } return content; }); } function fetchPageSubsection(language, sectionType, sectionOrdinal, subsection) { return fetchPageContent().then(function (content) { return readPageSubsection(content, language, sectionType, sectionOrdinal, subsection); }); } function fetchSenseFields(language, sectionType, sectionOrdinal, definitionLevel) { return fetchPageContent().then(function (content) { return readSenseFields(content, language, sectionType, sectionOrdinal, definitionLevel); }); } // Everything the advanced panel can edit about one sense, read off its definition line and the "#:" lines // under it: the raw gloss (templates included), the leading maneid/K/e values, and each word-list group. function readSenseFields(pageContent, language, sectionType, sectionOrdinal, definitionLevel) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var defMarker = /^#+/.exec(lines[defLine])[0]; var afterMarker = lines[defLine].slice(defMarker.length).replace(/^\s+/, ""); var leading = {}; splitLeadingCalls(afterMarker).calls.forEach(function (c) { leading[canonicalTemplateKey(c.key)] = c.params.slice(1); }); // Only start.js's "{{mane?|lang}}" placeholder is hidden; every other template stays visible so it can // be edited by hand in the gloss box. // The gloss box shows the line minus the calls that have their own box ({{maneid}}, {{K}}, {{e}} and // their redirects) and minus start.js's "{{mane?}}" placeholder. Everything else -- a {{g}} link, a // {{guharto}} -- stays visible and editable there. var split = splitLeadingCalls(afterMarker); var glossCalls = split.calls.filter(function (c) { return !hasOwnFieldBox(c.key) && canonicalTemplateKey(c.key) !== "mane?"; }); var glossLead = glossCalls.map(function (c) { return "{{" + c.key + "|" + c.params.join("|") + "}}"; }).join(" "); var gloss = $.trim((glossLead ? glossLead + " " : "") + split.rest); var marker = new Array(definitionLevel.length).join("#"); var groupKeysPattern = SENSE_GROUPS.map(function (g) { return aliasPattern(g.key); }).join("|"); var groupLineRegex = new RegExp("^" + marker + ":\\s*\\{\\{\\s*(" + groupKeysPattern + ")\\s*\\|([^}]*)\\}\\}\\s*$"); var anyBlockLineRegex = new RegExp("^" + marker + "[:*]"); var words = {}; for (var i = defLine + 1; i < lines.length; i++) { if (!anyBlockLineRegex.test(lines[i])) { break; } var m = groupLineRegex.exec(lines[i]); if (m) { words[canonicalTemplateKey(m[1])] = m[2].split("|").slice(1); } } return { gloss: gloss, maneid: leading.maneid ? leading.maneid[0] : "", etiket: leading.e || [], kategori: leading.K || [], words: words }; } // Finds a POS block's body range (up to, but not including, the next heading of level <= 3), and the // header text + line index of any "====" subsections it already contains. // depth is the part-of-speech heading's own ("===" on most entries, "====" where the senses are // grouped under numbered etymologies). Its block runs to the next heading at that depth or shallower, // and its subsections are the headings one deeper. function posBlockBounds(lines, posLineIndex, depth) { depth = depth || 3; var blockEnd = wt.headingAtOrAbove(depth); var subsectionHeading = wt.headingAtDepth(depth + 1); var end = lines.length; var existingHeaders = []; for (var i = posLineIndex + 1; i < lines.length; i++) { if (blockEnd.test(lines[i])) { end = i; break; } var sub = subsectionHeading.exec(lines[i]); if (sub) { existingHeaders.push({ header: $.trim(sub[1]), lineIndex: i }); } } return { end: end, existingHeaders: existingHeaders, depth: depth }; } // Looks up a header's rank in SUBSECTION_ORDER. -1 for a header that isn't one of ours; such a header is // never used as an insertion anchor. function subsectionRankForHeader(header) { return SUBSECTION_ORDER.indexOf(header); } // Body line range of one "====" subsection inside a POS block: everything after its heading up to the next // "====" heading, or the end of the block. Null if the block has no such subsection. function subsectionBodyRange(bounds, header) { var headers = bounds.existingHeaders; for (var i = 0; i < headers.length; i++) { if (headers[i].header === header) { return { start: headers[i].lineIndex + 1, end: i + 1 < headers.length ? headers[i + 1].lineIndex : bounds.end }; } } return null; } // ---- template-parameter editing ------------------------------------------------------------------- // A gloss that is nothing but one template call gets a labelled box per parameter instead of a raw // wikitext line, the way VisualEditor's template dialog works. // The single template call a gloss is made of, or null when the line is anything less tidy. {{g|...}} is a // link inside the gloss rather than the gloss itself, so it neither disqualifies the line nor gets a form. function glossTemplateTarget(gloss) { var parsed = parseTemplateCalls(gloss || ""); if (parsed.otherContent) { return null; } var editable = parsed.calls.filter(function (c) { return canonicalTemplateKey(c.name) !== "g"; }); return editable.length === 1 ? editable[0] : null; } // ---- the three places that carry no "====" heading of their own ---------------------------------- // Bilêvkirin is a "===" section above the POS blocks, images sit at the top of a POS block, and the // h-bnr hatnote goes above the first language heading. Each is located here and then read, written or // inserted through the same three lines of splice, so nothing below needs to know about them. var IMAGE_LINE = /^\[\[\s*(?:Wêne|File|Image|Dosya)\s*:/i; /** * {lines, langSectionIndex, exists, start, end, insertAt} for a subsection carrying a "place", or null * for an ordinary one. start..end is the body as it stands; insertAt is where a missing one goes. */ function locateLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection) { var place = subsection.place; if (!place) { return null; } var i, end; if (place === "page-top") { // Above every language section, so the whole page is the search space. The call is found under // any of its names, and its own name is carried back so a save never renames it. var pageLines = pageContent.split("\n"); var call = wt.findHbnrCall(pageContent); if (call) { return { lines: pageLines, langSectionIndex: 0, exists: true, call: call, start: call.lineIndex, end: call.lineIndex + 1, insertAt: call.lineIndex }; } return { lines: pageLines, langSectionIndex: 0, exists: false, start: 0, end: 0, insertAt: 0 }; } var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; if (place === "pron") { // One per language, not one per part of speech, so it is looked for above the POS heading. for (i = 0; i < loc.posLineIndex; i++) { var h3 = /^===\s*([^=]+?)\s*===/.exec(lines[i]); if (h3 && $.trim(h3[1]) === subsection.header) { end = i + 1; while (end < lines.length && !/^==/.test(lines[end])) { end++; } return { lines: lines, langSectionIndex: loc.langSectionIndex, exists: true, start: i + 1, end: end, insertAt: i }; } } // Straight after the language heading, which puts it before every POS block. return { lines: lines, langSectionIndex: loc.langSectionIndex, exists: false, start: 1, end: 1, insertAt: 1 }; } if (place === "pos-top") { // Between the conjugation line and the headword template -- where the wizard writes them. var at = loc.posLineIndex + 1; while (at < lines.length && /^\{\{\s*ku-tewîn/.test($.trim(lines[at]))) { at++; } var from = at; while (at < lines.length && IMAGE_LINE.test($.trim(lines[at]))) { at++; } return { lines: lines, langSectionIndex: loc.langSectionIndex, exists: at > from, start: from, end: at, insertAt: from }; } return null; } // The wikitext a "place" section's content becomes. A word list there is one template call, not a // {{kol3}} block, so the words are joined into it -- and any ⊕ modifier a row picked up is dropped, // since a page name is all the call can carry. function loosePlaceBody(subsection, content, language) { if (subsection.kind === "images") { return $.grep($.map(content || [], function (image) { return wt.buildImageLine({ file: image.file, caption: wt.captionForLang(image.caption, language), alt: image.alt, size: image.size, extra: image.extra }); }), Boolean).join("\n"); } return content; } // The words a "place" word list holds, flattened out of its single group. Any ⊕ modifier a row picked // up is dropped: a page name is all the call can carry. function loosePlaceWords(content) { var words = []; $.each(content || [], function (_, group) { $.each(group.words || [], function (_, raw) { var plain = $.trim(util.parseWordValue(raw).word); if (plain && $.inArray(plain, words) === -1) { words.push(plain); } }); }); return words; } // Writes one of those places, inserting it when it is not there yet and replacing it when it is. Only // Bilêvkirin carries a heading; the other two are bare lines. function writeLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection, content) { var at = locateLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection); if (!at) { throw new SubmitAbort("Cih nehate dîtin: " + subsection.header); } // A call naming a subpage -- "{{h-bnr/-ane}}" -- holds a list shared by every page transcluding // it. Writing here would change this page alone and desync the rest, so the edit is refused and // the template that actually owns the list is named instead. if (at.call && at.call.subpage) { throw new SubmitAbort("Ev rûpel «{{" + at.call.name + "}}» bi kar tîne — lîste li " + "[[Şablon:" + at.call.subpage + "]] ye, lê wê zêde bike, ne li vê rûpelê."); } // A word list here is one template call, so it is built where the existing call's own name is // known -- editing "{{bnr-h|...}}" must not turn it into "{{h-bnr|...}}". var body = subsection.kind === "wordlist" ? wt.buildHbnrCall(loosePlaceWords(content), at.call && at.call.name) : $.trim(content || ""); // A "===" section is separated from what is above and below it by a blank line, the way the rest // of an entry is written. The other two places are bare lines inside a block and take none. var heading = subsection.place === "pron"; function blank(index) { return $.trim(at.lines[index] || "") === ""; } if (at.exists) { if (body) { var replacement = [body]; // end is the next "==" heading; the blank line before it is inside the range being replaced. if (heading && /^==/.test(at.lines[at.end] || "")) { replacement.push(""); } at.lines.splice.apply(at.lines, [at.start, at.end - at.start].concat(replacement)); } else { // Emptying the box removes the section rather than leaving an empty heading behind. at.lines.splice(at.start, at.end - at.start); if (heading) { at.lines.splice(at.insertAt, 1); // The blank line that separated it goes with it, rather than doubling up. if (blank(at.insertAt) && blank(at.insertAt - 1)) { at.lines.splice(at.insertAt, 1); } } } } else { if (!body) { throw new SubmitAbort("Tiştek nehate nivîsandin."); } var block = heading ? ["=== " + subsection.header + " ===", body] : [body]; if (heading) { if (!blank(at.insertAt - 1)) { block.unshift(""); } if (!blank(at.insertAt)) { block.push(""); } } at.lines.splice.apply(at.lines, [at.insertAt, 0].concat(block)); } return pageContent.slice(0, at.langSectionIndex) + at.lines.join("\n"); } // What a section body holds: its {{kol3}} groups, the plain bullet list that stands in for them, or // the raw wikitext when it is neither. A free section is read this way before its kind is known. function readSubsectionBody(body, subsection) { if (subsection.kind === "wordlist" || subsection.free) { var calls = parseWordListCalls(body); if (calls.length) { return { kind: "wordlist", groups: calls.map(function (call) { return { sernav: namedParam(call.named, "sernav"), words: call.words }; }) }; } var bullets = parseBulletWordList(body); if (bullets) { // Editable as a word list; saving rewrites the bullets as {{kol3}}. return { kind: "wordlist", groups: [{ sernav: "", words: bullets }], fromBullets: true }; } } return { kind: "text", text: $.trim(body) }; } // The wikitext a section body becomes after an edit. A wordlist keeps its own template name, language // and named params ("cure=Jê", ...) and only has its positional words replaced; a text one is // replaced whole. function rewriteSubsectionBody(body, language, subsection, content) { if (subsection.kind !== "wordlist") { // Replacing the body wholesale drops that separator, so put it back. return content + (/\n\s*$/.test(body) ? "\n" : ""); } var calls = parseWordListCalls(body); if (calls.length) { // Each call is rewritten where it stands, so text between and around them survives untouched. // Last to first, so the offsets of the earlier ones stay valid as we go. var replacement = body; for (var gi = calls.length - 1; gi >= 0; gi--) { var call = calls[gi]; var group = content[gi]; // A group emptied in the form drops its call entirely rather than leaving "{{kol3|ku}}". var rebuilt = group && group.words.length ? buildWordListCall(call.name, call.lang, withNamedParam(call.named, "sernav", group.sernav), group.words) : ""; replacement = replacement.slice(0, call.start) + rebuilt + replacement.slice(call.end); } // Groups the form added beyond the ones already there go after the last of them. var added = content.slice(calls.length).filter(function (group) { return group.words.length; }).map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + subsection.header], "sernav", group.sernav), group.words); }); if (added.length) { replacement = replacement.replace(/\s*$/, "") + "\n\n" + added.join("\n\n") + "\n"; } return replacement.replace(/\n{3,}/g, "\n\n"); // gaps left by any dropped call } if (parseBulletWordList(body)) { // A plain bullet list is replaced wholesale by a real {{kol3}} on its first edit. return content.map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + subsection.header], "sernav", group.sernav), group.words); }).join("\n\n") + (/\n\s*$/.test(body) ? "\n" : ""); } throw new SubmitAbort("Nikare lîsteya vê beşê bixwîne: " + subsection.header); } // ---- sections that are not subsections of a part of speech ---- // A "=== Etîmolojî ===" that the parts of speech hang under, a "=== Çavkanî ===" at the foot of a // language, a heading nobody planned for. Located by its own text, and edited as the wikitext under // it -- or as a {{kol3}} list when that is what it holds. function freeSectionBody(pageContent, language, subsection) { var loc = wt.locateHeadingByText(pageContent, language, subsection.header, subsection.free.ordinal); return { loc: loc, body: loc.lines.slice(loc.bodyStart, loc.bodyEnd).join("\n") }; } function readFreeSection(pageContent, language, subsection) { var at = freeSectionBody(pageContent, language, subsection); return readSubsectionBody(at.body, subsection); } function updateFreeSection(pageContent, language, subsection, content) { var at = freeSectionBody(pageContent, language, subsection); at.loc.lines.splice(at.loc.bodyStart, at.loc.bodyEnd - at.loc.bodyStart, rewriteSubsectionBody(at.body, language, subsection, content)); return pageContent.slice(0, at.loc.langSectionIndex) + at.loc.lines.join("\n"); } // ---- removing and renaming a heading ---- // A subsection's heading line and the end of what it owns, as {loc, heading, end, depth, bounds}. A // "====" subsection runs to the next one, taking deeper headings with it. A free section owns only its // own text: what sits under it belongs to the parts of speech (see removeSubsection). function headingSpan(pageContent, language, sectionType, sectionOrdinal, subsection) { if (subsection.free) { var free = wt.locateHeadingByText(pageContent, language, subsection.header, subsection.free.ordinal); return { loc: free, heading: free.headingLineIndex, end: free.bodyEnd, depth: free.depth }; } var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var bounds = posBlockBounds(loc.lines, loc.posLineIndex, loc.depth); var range = subsectionBodyRange(bounds, subsection.header); if (!range) { throw new SubmitAbort(util.msg.sectionMissing + subsection.header); } return { loc: loc, heading: range.start - 1, end: range.end, depth: bounds.depth + 1, bounds: bounds }; } // A free section with deeper headings under it is refused: removing it would leave them under the // section before, and taking them along would remove whole parts of speech. function hasDeeperHeadingAfter(lines, index, depth) { var m = /^(=+)[^=]/.exec(lines[index] || ""); return !!m && m[1].length > depth; } function removeSubsection(pageContent, language, sectionType, sectionOrdinal, subsection) { var span = headingSpan(pageContent, language, sectionType, sectionOrdinal, subsection); var lines = span.loc.lines; if (subsection.free && hasDeeperHeadingAfter(lines, span.end, span.depth)) { throw new SubmitAbort("Di bin «" + subsection.header + "» de beşên din hene; pêşî wan jê bibe."); } lines.splice(span.heading, span.end - span.heading); // The blank line that separated it goes with it, rather than doubling up. if (span.heading > 0 && !$.trim(lines[span.heading - 1]) && !$.trim(lines[span.heading] || "x")) { lines.splice(span.heading, 1); } return pageContent.slice(0, span.loc.langSectionIndex) + lines.join("\n"); } function renameSubsection(pageContent, language, sectionType, sectionOrdinal, subsection, newHeader) { var span = headingSpan(pageContent, language, sectionType, sectionOrdinal, subsection); var lines = span.loc.lines; if (span.bounds && span.bounds.existingHeaders.some(function (h) { return h.header === newHeader; })) { throw new SubmitAbort("Ev beş jixwe heye: " + newHeader); } var marks = wt.headingMarks(span.depth); lines[span.heading] = marks + " " + newHeader + " " + marks; // A {{kol3}} names its section in cure=, which follows the heading. Read across lines: a call is // often written one parameter per line. var cure = new RegExp("(\\|\\s*cure\\s*=\\s*)" + escapeRegExp(subsection.header) + "(\\s*[|}])", "g"); var body = lines.slice(span.heading + 1, span.end).join("\n").replace(cure, "$1" + newHeader + "$2"); lines.splice.apply(lines, [span.heading + 1, span.end - span.heading - 1].concat(body.split("\n"))); return pageContent.slice(0, span.loc.langSectionIndex) + lines.join("\n"); } // Current contents of an existing "====" subsection. A wordlist one whose template can't be parsed comes // back as {kind: "text"} so the caller can still edit it as raw wikitext instead of refusing outright. function readPageSubsection(pageContent, language, sectionType, sectionOrdinal, subsection) { if (subsection.free) { return readFreeSection(pageContent, language, subsection); } var loose = locateLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection); if (loose) { if (!loose.exists) { return null; } if (loose.call) { return { kind: "wordlist", groups: [{ sernav: "", words: loose.call.words }] }; } var looseBody = $.trim(loose.lines.slice(loose.start, loose.end).join("\n")); if (subsection.kind === "images") { return { kind: "images", images: $.grep($.map(looseBody.split("\n"), wt.parseImageLine), Boolean) }; } if (subsection.kind !== "wordlist") { return { kind: "text", text: looseBody }; } // One template call rather than a {{kol3}} block: its positional parameters are the words. var looseCall = parseTemplateCalls(looseBody).calls[0]; return { kind: "wordlist", groups: [{ sernav: "", words: (looseCall && looseCall.params) || [] }] }; } var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var bounds = posBlockBounds(loc.lines, loc.posLineIndex, loc.depth); var range = subsectionBodyRange(bounds, subsection.header); if (!range) { return null; } return readSubsectionBody(loc.lines.slice(range.start, range.end).join("\n"), subsection); } // Rewrites an existing "====" subsection in place. A wordlist one keeps its own template name, language and // named params ("cure=Jê", ...) and only has its positional words replaced; a text one is replaced whole. function updatePageSubsection(pageContent, language, sectionType, sectionOrdinal, subsection, content) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var bounds = posBlockBounds(lines, loc.posLineIndex, loc.depth); var range = subsectionBodyRange(bounds, subsection.header); if (!range) { throw new SubmitAbort(util.msg.sectionMissing + subsection.header); } var body = lines.slice(range.start, range.end).join("\n"); lines.splice(range.start, range.end - range.start, rewriteSubsectionBody(body, language, subsection, content)); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Inserts a new "==== Header ====" subsection into its SUBSECTION_ORDER position within a POS block, right // before the first ranked subsection that outranks it, or at the end if none does. Either a // {{kol3|...}} word list or a plain paragraph, matching start.js's own subsection formats. function insertPageSubsection(pageContent, language, sectionType, sectionOrdinal, subsection, content) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var bounds = posBlockBounds(lines, loc.posLineIndex, loc.depth); var alreadyThere = false; bounds.existingHeaders.forEach(function (h) { if (h.header === subsection.header) { alreadyThere = true; } }); if (alreadyThere) { throw new SubmitAbort("Ev beş jixwe heye: " + subsection.header); } var body = subsection.kind === "wordlist" ? content.map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + subsection.header], "sernav", group.sernav), group.words); }).join("\n\n") : content; var myRank = subsectionRankForHeader(subsection.header); var headers = bounds.existingHeaders; var insertAt = bounds.end; for (var i = 0; i < headers.length; i++) { var otherRank = subsectionRankForHeader(headers[i].header); if (otherRank !== -1 && otherRank > myRank) { insertAt = headers[i].lineIndex; break; } } // Exactly one blank line on each side: the line before the insertion point is normally already blank // (it separated the previous subsection from the next heading), so only add one where there isn't one. var block = []; if (insertAt > 0 && $.trim(lines[insertAt - 1]) !== "") { block.push(""); } var marks = wt.headingMarks(bounds.depth + 1); block.push(marks + " " + subsection.header + " " + marks, body); if (insertAt < lines.length) { block.push(""); } lines.splice.apply(lines, [insertAt, 0].concat(block)); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // ---- converting an unclassified "=== Mane ===" placeholder heading into a real POS heading ---- // Some pages carry a heading literally named "Mane" (optionally "Mane 2" etc.) instead of a real // part-of-speech name, tagged right below with {{cure?|<lang>}} (ku.wiktionary's "part of speech not yet // determined" maintenance flag). It is a section key of its own (sections.MANE_SECTION), so the shared // locator finds it like any other heading; only the rewriting below is particular to it. // Renames the "=== Mane ===" placeholder to the chosen POS heading, drops the maintenance tag line right // after it if present (MANE_TAG_REGEX -- cure? or any of its known aliases), and inserts the matching // "{{<pos>|<lang>}}" call in its place (skipped when the section has no template of its own). // Other content under the heading is left untouched. /** * Where a section's own headword template sits, as {index, text}, or {index: -1} when it has none. * * Not always the line under the heading: a "{{ku-tewîn-lk}}" line and any number of "[[Wêne:...]]" * images can come first. Everything down to the first definition line is searched, and index doubles * as the slot a new call belongs in when there is no old one. */ function findPosTemplateLine(lines, headingLine, sectionType) { var oldSpec = posTemplateFor(sectionType); var oldTemplate = oldSpec ? oldSpec.name : (sectionType === "Biwêj" ? "biwêj" : null); var oldTemplateRegex = oldTemplate && new RegExp("^\\{\\{\\s*" + escapeRegExp(oldTemplate) + "\\s*[|}]"); var at = headingLine + 1; while (at < lines.length && !/^#/.test(lines[at]) && !/^==/.test(lines[at])) { if (MANE_TAG_REGEX.test(lines[at]) || (oldTemplateRegex && oldTemplateRegex.test(lines[at]))) { return { index: at, text: lines[at], end: at }; } at++; } return { index: -1, text: "", end: at }; } // Named parameters merged in order, each written once: a later value replaces an earlier one of the // same name. Positional values are kept as they come. function mergeCallParams() { var out = []; var seen = {}; $.each(Array.prototype.slice.call(arguments), function (_, list) { $.each(list || [], function (__, param) { var m = /^\s*([^=|]+?)\s*=([\s\S]*)$/.exec(param); if (!m) { out.push(param); return; } // "z" and "c" are one name to Modul:rêziman, so the form's "c=m" meets the page's "z=". var key = wt.canonicalParamName(m[1]); if (!Object.prototype.hasOwnProperty.call(seen, key)) { seen[key] = out.length; out.push(param); return; } // The page's own spelling stays when it carried a value; an empty one gives way entirely. var earlier = /^\s*([^=|]+?)\s*=([\s\S]*)$/.exec(out[seen[key]]); // "?" asks for a value, so it never replaces one the page already has. if ($.trim(earlier[2]) && $.trim(m[2]) === "?") { return; } out[seen[key]] =$.trim(earlier[2]) ? earlier[1] + "=" + m[2] : param; }); }); return out; } // What the call on the page already says, minus its language. Carried over only when the new section // uses that same template -- "z=m" survives Navdêr becoming Formeke navdêrê, while a verb's "c=h-gh" // has no meaning on a noun and is left behind. function keptCallParams(line, templateName) { var call = parseTemplateCalls($.trim(line || "")).calls[0]; if (!call || canonicalTemplateKey(call.name) !== canonicalTemplateKey(templateName)) { return []; } return call.params.slice(1).filter(function (param) { return /=/.test(param); }); } // The headword call a converted section gets, or "" when the new section has none. What the old call // (found, from findPosTemplateLine) said keeps its place, then the section's own defaults, then what the // form was filled in with -- each name written once, the last value winning, so a converted call reads // as small a change from the old one as it can. function posCallLine(found, language, newSectionType, posParams) { var spec = posTemplateFor(newSectionType); if (!spec) { return ""; } var kept = found.index !== -1 ? keptCallParams(found.text, spec.name) : []; // A lemma is not an inflected form, so the old section's form=erê stays behind. if (spec.params.indexOf("form=erê") === -1) { kept = kept.filter(function (param) { return !/^\s*form\s*=\s*erê\s*$/.test(param); }); } var params = mergeCallParams(kept, spec.params, posParams ? posParams.split("|") : []); return "{{" + spec.name + "|" + language + (params.length ? "|" + params.join("|") : "") + "}}"; } function convertSectionToPos(pageContent, language, sectionType, sectionOrdinal, newSectionType, posParams) { if (!sectionNames[newSectionType]) { throw new SubmitAbort("Navê cureyê rêzimanî yê hilbijartî nediyar e."); } // locateSection() already handles the "Mane" placeholder as a section type of its own. var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var headingLine = loc.posLineIndex; var depth = loc.depth; // A section straight under a numbered group heading takes that heading's place: the group heading // held nothing but its gloss. var group = depth > 3 ? wt.numberedGroupAbove(lines, headingLine, depth) : -1; if (group !== -1) { lines.splice(group, headingLine - group); headingLine = group; depth--; } // At the heading's own depth. Written without a number: the renumbering pass below decides every // number in this language section at once, so one carried over from the old name would just be // overwritten. var marks = wt.headingMarks(depth); lines[headingLine] = marks + " " + newSectionType + " " + marks; // The line under the heading is either the old section's own template call ("{{biwêj|ku}}") or, for a // Mane placeholder, the "needs a POS" maintenance tag. Both are replaced by the new POS's call. // The new call takes the old one's own place -- or, when there was none, the slot just above the // definitions, which is where the wizard writes it. var found = findPosTemplateLine(lines, headingLine, sectionType); var insertAt = found.index !== -1 ? found.index : found.end; if (found.index !== -1) { lines.splice(found.index, 1); } var call = posCallLine(found, language, newSectionType, posParams); if (call) { lines.splice(insertAt, 0, call); } // Renaming a section can make it repeat one the page already had, and can equally leave a name that // is no longer repeated. Both are settled by renumbering the whole language section: a "Rengdêr" // beside a new "Rengdêr" becomes "Rengdêr 1" and "Rengdêr 2", and a number left behind by a name // that is now unique is dropped. var body = lines.join("\n"); var nextLang = body.indexOf("\n== ", 1); var end = nextLang === -1 ? body.length : nextLang; body = wt.numberRepeatedPosHeadings(body.slice(0, end)) + body.slice(end); return pageContent.slice(0, loc.langSectionIndex) + body; } // ---- "guhartina pêşketî" (advanced editing): combine several independent per-sense edits into ONE save ---- // Threads pageContent through the same pure functions every single-field form uses, applying only the // pieces the caller actually filled in. A piece that conflicts with the page (maneid already set, a // subsection that's already there) is skipped with a warning rather than failing the whole save. Throws // SubmitAbort if nothing was filled in, so this can never turn into a no-op save. function applyAdvancedChanges(pageContent, language, sectionType, sectionOrdinal, definitionLevel, changes) { var content = pageContent; var warnings = []; var appliedAnything = false; if (changes.gloss) { content = updateSenseGloss(content, language, sectionType, sectionOrdinal, definitionLevel, changes.gloss); appliedAnything = true; } if (changes.newSense) { content = insertNewSense(content, language, sectionType, sectionOrdinal, definitionLevel, changes.newSense, changes.newSenseExtras); appliedAnything = true; } // Every box below is prefilled with what the sense already has, so a save replaces that value rather // than merging into it -- and clearing a box removes the template. changes.changed marks the boxes // whose contents actually differ from the page, so an opened-but-untouched box writes nothing. var replace = { replace: true }; if (changes.changed.maneid) { content = insertLeadingSenseTpl(content, language, sectionType, sectionOrdinal, definitionLevel, "maneid", changes.maneid ? [changes.maneid] : [], replace); appliedAnything = true; } if (changes.changed.etiket) { content = insertLeadingSenseTpl(content, language, sectionType, sectionOrdinal, definitionLevel, "e", changes.etiket, replace); appliedAnything = true; } if (changes.changed.kategori) { content = insertLeadingSenseTpl(content, language, sectionType, sectionOrdinal, definitionLevel, "K", changes.kategori, replace); appliedAnything = true; } SENSE_GROUPS.forEach(function (group) { if (changes.changed["word:" + group.key]) { content = insertSenseWords(content, language, sectionType, sectionOrdinal, definitionLevel, group.key, changes.words[group.key], replace); appliedAnything = true; } }); if (!appliedAnything) { throw new SubmitAbort("Tiştek nehat guhartin."); } return { content: content, warnings: warnings }; } // ---- per-definition UI: a small always-visible "+" toggle (no hover, so it works on mobile/touch too) ---- // A growing list of word rows. The row itself is util.js's, shared with the wizard; this owns the list: // typing in the last row appends a fresh empty one, so there is always somewhere to type. var WORDLIST_COLUMN_THRESHOLD = 6; function buildWordListField(placeholder, opts) { opts = opts || {}; var $list = $("<ol class='dsb-wordlist'></ol>"); // One column is a long scroll once a list gets past a handful of words. The class only asks for // two; the stylesheet still gives one on a narrow screen. Two columns inside the panel's usual // 520px would leave each word about 250px, so the panel drops its width cap for as long as any // list in it is in two columns -- checked across the whole panel, since a "Herwiha binêre" holds // several groups and one of them going wide settles it for all. function updateColumns() { $list.toggleClass("dsb-wordlist-2col", $list.children("li").length > WORDLIST_COLUMN_THRESHOLD); var $panel = $list.closest(".dsb-subsection-panel"); $panel.toggleClass("dsb-panel-wide", !!$panel.find(".dsb-wordlist-2col").length); } function makeRow() { var row = util.makeModifierRow({ prefix: "dsb-", placeholder: placeholder, modTitle: util.msg.addModifier, removeTitle: util.msg.removeWord, removeModTitle: util.msg.removeModifier, modifiers: opts.modifiers, // "bixe qutiyan" puts each comma-separated word in a row of its own, right after the row it // was typed in, and the caret follows to the last of them. One word per row is also one // word per template parameter, which both {{hevmane}} and {{kol3}} read the same way -- // a comma left inside a parameter would not be. onSplit: function (rest) { var $after = row.$li; var last = null; rest.forEach(function (raw) { var extra = makeRow(); $after.after(extra.$li); $after = extra.$li; if (raw) extra.setRaw(raw); last = extra; }); // Always an empty row at the end to type the next word into. var $tail = $list.children("li").last(); if ($.trim($tail.data("row").getRaw())) { last = makeRow(); $list.append(last.$li); } updateColumns(); if (last) last.focus(); }, // raw === null means the row removed itself, so the count has changed either way. onChange: function (raw) { if (raw !== null) { var isLast = row.$li.is($list.children("li").last()); if (isLast && raw) $list.append(makeRow().$li); } updateColumns(); } }); row.$li.data("row", row); return row; } $list.append(makeRow().$li); return { $element: $list, values: function () { var vals = []; $list.children("li").each(function () { var raw = $(this).data("row").getRaw(); if (raw) vals.push(raw); }); return vals; }, // Replaces the whole list, keeping one empty trailing row to type into. setValues: function (raws) { $list.empty(); raws.forEach(function (raw) { var row = makeRow(); $list.append(row.$li); row.setRaw(raw); }); $list.append(makeRow().$li); updateColumns(); }, focus: function () { $list.children("li").first().data("row").focus(); } }; } // Plain removable-tag field, used for Etîket and Kategorî (simple value lists with no per-word modifiers). // Enter or "," commits the current text as a tag; Backspace on an empty input removes the last tag; // values() also commits whatever is still typed but not yet confirmed. function buildTagField(placeholder) { var $input = $("<input type='text' class='dsb-input dsb-tag-input'>").attr("placeholder", placeholder); var $element = $("<span class='dsb-tagfield'></span>").append($input); function currentValues() { var vals = []; $element.children(".dsb-tag").each(function () { vals.push($(this).data("value")); }); return vals; } function addTag(val) { val = $.trim(val || ""); if (!val || currentValues().indexOf(val) !== -1) { return; } var $tag = $("<span class='dsb-tag'></span>").text(val).data("value", val); var $remove = $("<button type='button' class='dsb-tag-remove' title='Jê bibe'>&#215;</button>"); $remove.on("click", function () { $tag.remove(); $input.trigger("focus"); }); $tag.append($remove); $tag.insertBefore($input); } $input.on("keydown", function (e) { if (e.which === 13 || e.which === 188) { // Enter or comma e.preventDefault(); addTag($input.val()); $input.val(""); } else if (e.which === 8 && !$input.val()) { $element.children(".dsb-tag").last().remove(); } }); $input.on("blur", function () { addTag($input.val()); $input.val(""); }); return { $element: $element, // Replaces every tag, for prefilling from what the sense already has. setValues: function (vals) { $element.children(".dsb-tag").remove(); vals.forEach(function (v) { addTag(v); }); $input.val(""); }, values: function () { addTag($input.val()); $input.val(""); return currentValues(); }, focus: function () { $input.trigger("focus"); } }; } // The wizard's own etymology templates, straight out of data-main.json's "etymTpls" -- the same list start.js's // insert menu offers, so the two can never drift apart. CODE/SOURCELANG are its placeholders. // The wizard's own template lists, straight out of data-main.json, so both gadgets offer the same // templates in the same order. A pronunciation template names the languages it is for; one that names // none is offered everywhere. function templateChoices(pool, language) { var defs = startData[pool] || {}; return Object.keys(defs).filter(function (name) { var langs = defs[name].langs; return !langs || !langs.length || $.inArray(language, langs) !== -1; }).map(function (name) { return { name: name, desc: defs[name].desc, snippet: defs[name].snippet }; }); } // One template call inside the surface: shown as its own wikitext, but atomic -- contenteditable="false" // keeps the caret out of it, so it can only be changed through the dialog. function makeTemplateChip(wikitext) { // The × is its own element rather than the chip's only way out: on a phone there is no reliable // backspace onto a contenteditable="false" span, so without it the dialog is the only way to take // a call off the line. readChipSurface() reads chips from data-wikitext, so the extra child does // not reach the saved text. var $remove = $("<span class='dsb-tpl-chip-remove' contenteditable='false'>&#215;</span>") .attr("title", "Vê şablonê jê bibe"); return $("<span class='dsb-tpl-chip' contenteditable='false' role='button' tabindex='0'></span>") .attr("data-wikitext", wikitext) .attr("title", "Bitikîne da ku parametreyan biguhêrî") .text(wikitext) .append($remove); } // wikitext -> surface DOM: template calls become chips, everything else stays plain editable text. function renderChipSurface($surface, wikitext) { var text = wikitext || ""; var calls = parseTemplateCalls(text).calls; var pos = 0; $surface.empty(); calls.forEach(function (call) { if (call.start > pos) { $surface.append(document.createTextNode(text.slice(pos, call.start))); } $surface.append(makeTemplateChip(text.slice(call.start, call.end))); pos = call.end; }); if (pos < text.length) { $surface.append(document.createTextNode(text.slice(pos))); } } // surface DOM -> wikitext. Chips give back their stored call; anything the browser inserted while typing // (a <br>, a wrapper <div>) degrades to its text, so the result is always plain wikitext. function readChipSurface(node) { var out = ""; $(node).contents().each(function () { if (this.nodeType === 3) { out += this.nodeValue; return; } var $el = $(this); if ($el.hasClass("dsb-tpl-chip")) { out += $el.attr("data-wikitext"); return; } if (this.nodeName === "BR") { out += "\n"; return; } if (this.nodeName === "DIV" || this.nodeName === "P") { out += "\n" + readChipSurface(this); return; } out += readChipSurface(this); }); return out; } // Drops a node in at the caret, or at the end when the caret is not inside the surface. function insertAtCaret($surface, node) { // Inserting a fragment moves its children into the surface and leaves the fragment itself empty // and parentless, so the caret follows that last child; setStartAfter on the fragment throws. var last = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? node.lastChild : node; var sel = window.getSelection(); if (sel && sel.rangeCount && $.contains($surface.get(0), sel.getRangeAt(0).commonAncestorContainer)) { var range = sel.getRangeAt(0); range.deleteContents(); range.insertNode(node); if (last) { range.setStartAfter(last); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } } else { $surface.append(node); } } // The template-param dialog (openTemplateDialog/buildTemplateParamForm) moved to wikt.cite, shared with // start.js's own Etîmolojî and Çavkanî pickers: cite.openTemplateDialog / cite.buildTemplateParamForm. // Each new example queued is its own entry, so several can be waiting at once. var exampleSeq = 0; // Takes the save over from add-examples while its form is open for one sense: its "Qeyd bike" becomes // "Lê zêde bike" and the edit joins the pending queue instead of publishing on its own, so examples and // whatever else is queued go up as one edit. Installed on every open, so the handler always closes over // the sense whose form is actually open. ctx: {$li, $h3, language, sectionType, sectionOrdinal, // definitionLevel, $save, $status, afterQueue}. function takeOverExampleSave(ctx) { if (!(wikt.addExamples && wikt.addExamples.setSubmitHandler)) { return; // older add-examples: it publishes on its own, as before } // Lets the form read an example as the queue has already changed it. wikt.addExamples.pendingContent = queuedContent; wikt.addExamples.setSubmitHandler(function (job) { exampleSeq++; queueChange( placeLabel(ctx.language, ctx.sectionType, ctx.$h3), // add-examples' own summary, so the combined edit still credits the tool and names its // version. The trailing stop goes: this is one item in a list, not a sentence of its own. (job.summary || "nimûne/jêgirtin hate zêdekirin").replace(/\.\s*$/, ""), function (pageContent) { // add-examples builds against a revision object, and returns {text, summary}. var built = job.transform({ content: pageContent }); return { content: built.text, warnings: [] }; }, ctx.$save, ctx.$status, // Drawn like any other queued change, in the sense it belongs to -- except a removal, // which strikes the line through where it sits, as "Maneyê rake" does for a sense. function () { if (job.removes && ctx.itemEl) { var $item = $(ctx.itemEl).addClass("dsb-example-doomed"); return function () { $item.removeClass("dsb-example-doomed"); }; } return previewSense(ctx.$li, ctx.language, ctx.sectionType, ctx.sectionOrdinal, ctx.definitionLevel); }, // An edit to an example already on the page brings its own key, so editing it again replaces it. { key: job.key || ("example:" + ctx.sectionType + "|" + ctx.sectionOrdinal + "|" + ctx.definitionLevel.join(".") + "|" + exampleSeq), sensePath: ctx.definitionLevel } ); job.done(); if (ctx.afterQueue) { ctx.afterQueue(); } }); } // Opens add-examples' form on one example already under a sense -- dsb.js's pencil on it. Its save joins // the queue like any other change. function editExample($li, $h3, language, sectionType, sectionOrdinal, definitionLevel, itemEl) { takeOverExampleSave({ $li: $li, $h3: $h3, language: language, sectionType: sectionType, sectionOrdinal: sectionOrdinal, definitionLevel: definitionLevel, $save: $(), $status: $(), itemEl: itemEl, afterQueue: openQueueBox }); if (!(wikt.addExamples && wikt.addExamples.editFor && wikt.addExamples.editFor($li.get(0), itemEl))) { mw.notify("Amûra nimûneyan li vê maneyê nehat dîtin."); } } // Builds the whole "guhartina pêşketî" panel into $quickRow: gloss and the two most-used word lists // (Hevmane/Dijmane) sit directly in the picker row; the remaining word lists, maneid, etîket, kategorî // and Nimûne/jêgirtin live behind a "Hevmane û Bêhtir" dropdown; one shared "Lê zêde bike" queues everything the // user filled in as a single edit via applyAdvancedChanges() and queueChange(). Nimûne/jêgirtin is an // immediate proxy-click into a different gadget's own form, not part of this combined save. // pending, when given, means this panel composes a sense that is not on the page yet: it edits no // line, and its save queues one insertion carrying the definition, its word lists and its // quotations. Everything the user sees is the panel's own, so a new sense is edited exactly like a // saved one. See openNewSenseRow(). function buildAdvancedPanel($quickRow, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense, pending) { // "#:" lines for a pending sense's quotations, in the order add-examples handed them over. var pendingQuotes = pending ? pending.state.quotes.slice() : []; var $pendingQuotes = $("<div class='dsb-new-sense-quotes'></div>"); // "+" marks the actions that add something, matching the dropdown's own "+ Hevmane" labels. var $nimune = $("<a href='#' class='dsb-quick-link'></a>").text("+ Nimûne/jêgirtin"); $nimune.on("click", function (e) { e.preventDefault(); if (pending) { collectQuotation(); $moreMenu.slideUp(); return; } takeOverExampleSave({ $li: $li, $h3: $h3, language: language, sectionType: sectionType, sectionOrdinal: sectionOrdinal, definitionLevel: definitionLevel, $save: $save, $status: $status }); // Toggle, so clicking this a second time closes the form again. add-examples builds nothing // on the page of its own, so this is the only way its form opens. var drive = wikt.addExamples.toggleFor || wikt.addExamples.openFor; if (!(drive && drive($li.get(0)))) { mw.notify("Amûra nimûneyan li vê maneyê nehat dîtin."); } $moreMenu.slideUp(); }); // The form has to be opened on a sense that exists, so it opens on the one this row hangs off; // only the line it builds is kept, and the form itself is moved under the row, title and all. function collectQuotation() { if (!(wikt.addExamples && wikt.addExamples.setSubmitHandler)) { mw.notify("Amûra nimûneyan ne berdest e."); return; } wikt.addExamples.setSubmitHandler(function (job) { if (!job.line) { job.fail("Ev guhartoya amûra nimûneyan nikare rêzê bide."); return; } pendingQuotes.push(job.line.replace(/^#+/, "")); $pendingQuotes.append($("<div class='dsb-new-sense-quote'></div>").text(job.line)); job.done(); }); if (!(wikt.addExamples.openFor && wikt.addExamples.openFor($li.get(0)))) { mw.notify("Amûra nimûneyan li vê maneyê nehat dîtin."); return; } // Its title is a list item in the sense's own example list; taken out of that list so it // does not read as a numbered sense of its own. var $title = $li.find(".add-example-form-title").first().closest("li"); var $form = $li.find(".add-example-form").first(); if ($title.length) { $pendingQuotes.after($("<div class='dsb-new-sense-form-title'></div>") .append($title.contents())); $title.remove(); } if ($form.length) { $quickRow.find(".dsb-new-sense-form-title").first().after($form); } } // getters holds one function per currently-open box (key -> () => value); collectChanges() only ever // reads from a box the user actually clicked to open. var getters = {}; // What the sense currently holds, fetched once and shared by every box that prefills from it. // collectChanges() also diffs against it, so an untouched box never queues a change. var original = null; var sensePromise = null; function loadSense() { if (!sensePromise) { if (pending) { original = { gloss: pending.state.text || "", maneid: pending.state.maneid || "", etiket: pending.state.etiket || [], kategori: pending.state.kategori || [], words: pending.state.lists || {} }; sensePromise = $.Deferred().resolve(original).promise(); } else { sensePromise = fetchSenseFields(language, sectionType, sectionOrdinal, definitionLevel).then(function (fields) { original = fields; return fields; }); } } return sensePromise.then(withQueued); } // The change already queued for this sense, as the fields it writes. Read back from the queue // rather than kept in a variable here, so dropping the entry from the queue box also drops this. function queuedSenseChanges() { if (pending) { return null; } var key = senseQueueKey(); var found = null; dsbQueue.entries.forEach(function (entry) { if (entry.key === key && entry.senseChanges) { found = entry.senseChanges; } }); return found; } // The sense with whatever is queued for it written over the top (nothing queued: as the page has it). function withQueued(fields) { return overlaySenseFields(fields, queuedSenseChanges()); } function sameList(a, b) { return (a || []).join("\u0000") === (b || []).join("\u0000"); } // -- gloss ("Maneyê biguhêre" -- the one box that EDITS the sense's wording; everything else here only ADDS) -- function addGlossField() { var $fields = $("<div class='dsb-gloss-fields'></div>"); var $textarea = $("<textarea class='dsb-input dsb-edit-gloss' rows='2'></textarea>") .attr("placeholder", util.msg.loading).prop("disabled", true); var $autoLink = $("<button type='button' class='dsb-autolink'>Oto-lînk bike</button>").prop("disabled", true); var $toRaw = $("<a href='#' class='dsb-quick-link dsb-gloss-raw'>Wekî wîkîtekst biguhêre</a>").hide(); var $status = $("<span class='dsb-status'></span>"); var originalGloss = null; var tplForm = null; // set while the parameter boxes are showing instead of the raw line var tplCall = null; // The gloss of a non-Kurdish entry is a translation followed by {{îzah|…}} explaining it. That // is one field on the page, so it stays one box here; the explanation opens beside it only when // asked for, and the two are joined back into a single gloss on save. A Kurdish sense is its // own definition, so it is never offered one -- though a ku gloss that somehow already carries // an {{îzah}} still opens its box below, rather than being edited blind. var offerIzah = language !== "ku"; var $izah = $("<textarea class='dsb-input dsb-edit-izah' rows='2'></textarea>") .attr("placeholder", "Îzaha maneyî… ji bo school-a îngilîzî 'cihê tehsîlgirtinê'"); // A non-Kurdish gloss is a translation and its explanation, so both boxes open together. A Kurdish // sense is its own definition and gets no îzah box, unless its gloss already carries one. var $izahWrap = $("<div class='dsb-izah-wrap'></div>") .append($("<label class='dsb-adv-label'>Îzaha maneyî</label>").append($izah)) .toggle(offerIzah); $fields.append($textarea); // The template form edits one call inside the line, so its result is spliced back into the rest. function currentGloss() { if (tplForm) { return $.trim(originalGloss.slice(0, tplCall.start) + tplForm.values() + originalGloss.slice(tplCall.end)); } return joinGloss($.trim($textarea.val()), $.trim($izah.val())); } function showRaw() { tplForm = null; tplCall = null; $fields.empty().append($textarea); $autoLink.show(); $toRaw.hide(); } loadSense().then( function (fields) { originalGloss = fields.gloss; // An existing gloss that already ends in {{îzah|…}} comes apart into the two boxes, so // editing it does not mean editing template syntax by hand. var parts = util.splitTercimeIzah(fields.gloss); $textarea.val(parts ? $.trim(parts.tercime) : fields.gloss) .prop("disabled", false) // A non-Kurdish gloss is a translation; the box under it takes the explanation. .attr("placeholder", offerIzah ? util.msg.tercimePrompt : "Maneyê binivîse…"); if (parts) { $izah.val($.trim(parts.izah)); $izahWrap.show(); } $autoLink.prop("disabled", false); var target = glossTemplateTarget(fields.gloss); if (!target) { return; } fetchTemplateData(target.name).then(function (td) { // The fetch is async; leave the raw box alone if it has already been typed into. if ($.trim($textarea.val()) !== $.trim(originalGloss)) { return; } tplCall = target; tplForm = cite.buildTemplateParamForm(target, td); $fields.empty().append(tplForm.$element); $autoLink.hide(); // linkifying a bare template call makes no sense // A gloss that is one template call carries no separate explanation. $izahWrap.hide(); $toRaw.show(); }); }, function (codeOrAbort, result) { $textarea.attr("placeholder", ""); var message = codeOrAbort instanceof SubmitAbort ? codeOrAbort.message : "Nikare maneya heyî bar bike: " + apiErrorMessage(codeOrAbort, result); $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text(message); } ); $autoLink.on("click", function () { var mane = $.trim($textarea.val()); var izah = $.trim($izah.val()); if (!mane && !izah) { return; } $autoLink.prop("disabled", true); $status.removeClass("dsb-status-ok dsb-status-error").text("Tê lînkkirin…"); linkifyGlossParts(mane, izah, function (linkedMane, linkedIzah, error) { $textarea.val(linkedMane); $izah.val(linkedIzah); $autoLink.prop("disabled", false); if (error) { $status.addClass("dsb-status-error").text(error); } else { $status.text(""); } }); }); $toRaw.on("click", function (e) { e.preventDefault(); $textarea.val(currentGloss()); // read the form before showRaw() discards it showRaw(); }); getters.gloss = function () { var v = currentGloss(); // A pending sense has no line on the page to differ from: whatever is in the box is the sense. return (pending || (v && v !== originalGloss)) ? (v || null) : null; }; return $("<div class='dsb-adv-group'></div>").append( $("<div class='dsb-adv-label'></div>").text("Mane"), $fields, $izahWrap, $("<div class='dsb-form-actions'></div>").append($autoLink, $toRaw, $status) ); } // -- one SENSE_GROUPS word-list box (Hevmane/Dijmane/etc.) -- function addWordField(group) { var field = buildWordListField(group.label + "…"); loadSense().then(function (fields) { var current = fields.words[group.key]; if (current && current.length) { field.setValues(current); } }); getters["word:" + group.key] = function () { return field.values(); }; // The hint is senseGroups' own, so the box carries the same example sentence the wizard puts // beside its copy of this list. var $label = $("<div class='dsb-adv-label'></div>").text(group.label); if (group.hint) { $label.append($("<span class='dsb-adv-hint'></span>").text(group.hint)); } return $("<div class='dsb-adv-group'></div>").append($label, field.$element); } // -- maneid (single value; re-adding one that already exists is rejected -- see applyAdvancedChanges) -- // "Maneyeke din" opens a row of its own directly under this sense, so the list numbers it as the // sense it is about to become, and it carries its own buttons: everything it collects -- the // definition, its word lists, its quotations -- goes onto the page as a single insertion, because a // sense that is not saved yet has no line number for a second change to find (see insertNewSense). var newSenseKey = "newSense:" + definitionLevel.join("."); function queuedNewSense() { var found = null; dsbQueue.entries.forEach(function (entry) { if (entry.key === newSenseKey) { found = entry; } }); return found; } function dropQueuedNewSense() { dsbQueue.entries.slice().forEach(function (entry) { if (entry.key === newSenseKey) { dropQueueEntry(entry); } }); } // A sense that is not on the page yet is edited with this same panel, in a row of its own: same // picker, same dropdown, same buttons. Only the save differs -- it writes an insertion rather // than editing a line -- and the row renders what the insertion will say. function openNewSenseRow(state) { var existing = $quickRow.data("dsbNewSenseRow"); if (existing && existing.closest("body").length && !state) { existing.find("textarea").first().trigger("focus"); return; } if (existing) { existing.remove(); } if (state) { // The rendered row belongs to the queue entry: hide it while the box is open, and leave // the entry alone so cancelling can put it back. $li.nextAll("li.dsb-new-sense-pending").first().remove(); } openQueueBox(); var $row = $("<li class='dsb-sense-preview dsb-new-sense-row'></li>"); var $panel = $("<div class='dsb-quick-row dsb-new-sense'></div>"); $row.append($panel); $li.after($row); $quickRow.data("dsbNewSenseRow", $row); buildAdvancedPanel($panel, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense, { state: state || { text: "", lists: {}, quotes: [], maneid: "", etiket: [], kategori: [] }, key: newSenseKey, $row: $row, close: function () { $row.remove(); $quickRow.removeData("dsbNewSenseRow"); hideQueueIfIdle(); }, reopen: openNewSenseRow, queued: queuedNewSense, drop: dropQueuedNewSense }); $panel.show(); $panel.find("textarea").first().trigger("focus"); } function addManeidField() { var $input = $("<input type='text' class='dsb-input' placeholder='id…'>"); loadSense().then(function (fields) { $input.val(fields.maneid); }); getters.maneid = function () { return $.trim($input.val()); }; return $("<div class='dsb-adv-group'></div>").append($("<div class='dsb-adv-label'></div>").text("Maneid"), $input); } // -- etîket / kategorî -- function addEtiketField() { var field = buildTagField("Etîket…"); loadSense().then(function (fields) { field.setValues(fields.etiket); }); getters.etiket = function () { return field.values(); }; return $("<div class='dsb-adv-group'></div>").append($("<div class='dsb-adv-label'></div>").text("Etîket"), field.$element); } function addKategoriField() { var field = buildTagField("Kategorî…"); loadSense().then(function (fields) { field.setValues(fields.kategori); }); getters.kategori = function () { return field.values(); }; return $("<div class='dsb-adv-group'></div>").append($("<div class='dsb-adv-label'></div>").text("Kategorî"), field.$element); } // -- the picker row: the gloss and Nimûne/jêgirtin links sit directly in the row; every word group and // the rest live behind a "Hevmane û Bêhtir" dropdown. Clicking any link builds and appends its box // to $openBoxes and hides that link; the box's own "×" removes it, clears its getter, and un-hides the // link -- several boxes can be open at once. No "Beşek (====)" entry here: buildSubsectionPanel()'s // own "+ Beşek lê zêde bike" trigger already covers page-level subsections. ---- var FIELD_DEFS = [{ key: "gloss", label: GLOSS_LABEL, build: addGlossField, primary: true }] .concat(SENSE_GROUPS.map(function (group) { return { key: "word:" + group.key, label: group.label, build: function () { return addWordField(group); }, primary: false }; })) .concat([ { key: "maneid", label: "Maneid", build: addManeidField, primary: false }, { key: "etiket", label: "Etîket", build: addEtiketField, primary: false }, { key: "kategori", label: "Kategorî", build: addKategoriField, primary: false } ]); // Every sense offers this: the new one lands directly below whichever sense asked for it (see // insertNewSense), so it is no longer only the last one that can add to the end. // No build(): its link opens a row of its own (openNewSenseRow), not a box in this panel. FIELD_DEFS.push({ key: "newSense", label: util.msg.addAnotherSense, primary: false }); var $picker = $("<div class='dsb-field-picker'></div>"); var $moreMenu = $("<div class='dsb-more-menu'></div>").hide(); var $openBoxes = $("<div class='dsb-open-boxes'></div>"); // $actions (built below) stays hidden until at least one field box is actually open -- otherwise // "Lê zêde bike" sits there enabled with nothing to queue, which just produces an empty no-op change. var openBoxCount = 0; function updateActionsVisibility() { $actions.toggle(openBoxCount > 0); } function makeFieldLink(def) { var $link = $("<a href='#' class='dsb-quick-link dsb-field-pick'></a>").text(def.key === "gloss" ? def.label : "+ " + def.label); $link.on("click", function (e) { e.preventDefault(); if (def.key === "newSense") { $moreMenu.slideUp(); openNewSenseRow(); return; } openQueueBox(); var $box = def.build(); var $remove = $("<a href='#' class='dsb-box-remove' title='Rake'>×</a>"); $remove.on("click", function (e2) { e2.preventDefault(); delete getters[def.key]; $box.remove(); $link.show(); openBoxCount--; updateActionsVisibility(); }); $box.prepend($remove); $openBoxes.append($box); util.initManualResize($box); $link.hide(); $moreMenu.slideUp(); openBoxCount++; updateActionsVisibility(); }); return $link; } // Destructive, so it lives at the bottom of the dropdown behind a confirmation, and shares the // sense's queue key: deleting supersedes any edit still pending on the same sense. function makeDeleteLink() { var $link = $("<a href='#' class='dsb-quick-link dsb-field-delete'>Maneyê rake</a>"); $link.on("click", function (e) { e.preventDefault(); $moreMenu.slideUp(); if (pending) { // Nothing to remove from the page: the sense only exists in the queue. pending.drop(); pending.close(); return; } OO.ui.confirm("Ev mane û hemû rêzên bin wê dê werin rakirin. Dewam bike?").done(function (ok) { if (!ok) { return; } queueChange( placeLabel(language, sectionType, $h3), "maneyek hate rakirin", function (baseContent) { return removeSense(baseContent, language, sectionType, sectionOrdinal, definitionLevel); }, $save, $note, function () { $li.addClass("dsb-sense-doomed"); return function () { $li.removeClass("dsb-sense-doomed"); }; }, { key: senseQueueKey(), sensePath: definitionLevel, senseLanguage: language, removesSense: true } ); $note.show(); }); }); return $link; } // Only the links the row actually shows are built now; the dropdown's nine are 43% of the work and // nobody sees them until "Hevmane û Bêhtir" is clicked, so buildMenuLinks() waits for that. FIELD_DEFS.forEach(function (def) { if (!def.primary || (pending && def.key === "newSense")) { return; } $picker.append(makeFieldLink(def)); if (def.key === "gloss") { $picker.append($nimune); // directly after "Maneyê biguhêre" } }); // Three sections, ruled apart: what labels this sense, the word lists that hang off it, and adding // another sense. "Maneyê rake" is appended after these by makeDeleteLink() and so closes the last // one. The word lists follow senseGroups' "order" -- that array's own order is the order the "#:" // lines are written to the page in, which is not a reading order; the wizard's boxes read the same // field, so both gadgets list them alike. Anything not named here still appears, after these, so a // field added later is never silently dropped from the menu. var MENU_GROUPS = [ ["etiket", "kategori", "maneid"], SENSE_GROUPS.slice().sort(function (a, b) { return (a.order || 0) - (b.order || 0); }).map(function (g) { return "word:" + g.key; }), ["newSense"] ]; var menuBuilt = false; function buildMenuLinks() { if (menuBuilt) { return; } menuBuilt = true; var byKey = {}; FIELD_DEFS.forEach(function (def) { if (!def.primary && !(pending && def.key === "newSense")) { byKey[def.key] = def; } }); var placed = {}; MENU_GROUPS.forEach(function (group, groupIndex) { var defs = group.map(function (key) { return byKey[key]; }).filter(Boolean); defs.forEach(function (def, i) { placed[def.key] = true; var $link = makeFieldLink(def); // The rule goes on the first entry of every group after the first. if (groupIndex > 0 && i === 0) { $link.addClass("dsb-menu-group-start"); } $moreMenu.append($link); }); }); FIELD_DEFS.forEach(function (def) { if (!def.primary && !placed[def.key] && !(pending && def.key === "newSense")) { $moreMenu.append(makeFieldLink(def)); } }); $moreMenu.append(makeDeleteLink()); } // Lives inside $picker (not appended straight to $quickRow) so .dsb-more-menu's CSS can position it as // an overlay anchored to $picker, instead of pushing $openBoxes/the rest of the page down when it opens. var $moreArrow = $("<span class='dsb-more-arrow'></span>").text("▸"); var $moreToggle = $("<a href='#' class='dsb-quick-link dsb-more-toggle'></a>") .text(util.msg.moreMenu + " ").append($moreArrow); $moreToggle.on("click", function (e) { e.preventDefault(); buildMenuLinks(); $moreMenu.slideToggle(function () { $moreArrow.text($moreMenu.is(":visible") ? "▾" : "▸"); }); }); $picker.append($moreToggle, $moreMenu); // Outside clicks are handled by one shared document listener (see closeMoreMenus), not one per panel: // hovering a sense builds its panel, so a long entry would otherwise accumulate dozens of them. $moreMenu.data("dsbArrow", $moreArrow); // -- combined save: whatever boxes are open right now, queued together via queueChange(); the actual // save happens when the user clicks "Guhartinan qeyd bike" on the floating queue box. ---- // Small "not saved yet" preview of everything this queued change will do, appended right under the // sense's gloss (plain text, not a real render of {{hevmane}}/etc.). function collectChanges() { // Against what the boxes were filled from, queued change and all -- a box reopened and left // alone writes nothing, and one the user has just emptied counts as emptied. var base = withQueued(original || { maneid: "", etiket: [], kategori: [], words: {} }); var changed = {}; var words = {}; SENSE_GROUPS.forEach(function (group) { var getter = getters["word:" + group.key]; words[group.key] = getter ? getter() : []; if (getter && !sameList(words[group.key], base.words[group.key])) { changed["word:" + group.key] = true; } }); var maneid = getters.maneid ? getters.maneid() : ""; var etiket = getters.etiket ? getters.etiket() : []; var kategori = getters.kategori ? getters.kategori() : []; if (getters.maneid && maneid !== (base.maneid || "")) { changed.maneid = true; } if (getters.etiket && !sameList(etiket, base.etiket)) { changed.etiket = true; } if (getters.kategori && !sameList(kategori, base.kategori)) { changed.kategori = true; } return { gloss: getters.gloss ? getters.gloss() : null, maneid: maneid, etiket: etiket, kategori: kategori, words: words, changed: changed }; } // One insertion carrying the whole sense: its leading calls and definition on the "#" line, then // a "#:" line per word list and per quotation, in the order the wizard writes them. function newSenseWikitext(changes) { var leading = LEADING_TPL_ORDER.map(function (key) { var values = key === "maneid" ? (changes.maneid ? [changes.maneid] : []) : key === "e" ? changes.etiket : key === "K" ? changes.kategori : []; return values && values.length ? "{{" + key + "|" + language + "|" + values.join("|") + "}}" : null; }).filter(Boolean).join(" "); var lines = []; SENSE_GROUPS.forEach(function (group) { var words = changes.words[group.key] || []; if (words.length) { lines.push(": {{" + group.key + "|" + language + "|" + words.join("|") + "}}"); } }); lines = lines.concat(pendingQuotes); return { text: (leading ? leading + " " : "") + $.trim(changes.gloss || ""), extras: lines }; } function pendingState(changes) { return { text: $.trim(changes.gloss || ""), lists: changes.words, quotes: pendingQuotes.slice(), maneid: changes.maneid, etiket: changes.etiket, kategori: changes.kategori }; } function queueNewSense(changes) { if (!$.trim(changes.gloss || "")) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Maneya nû vala ye."); return; } var built = newSenseWikitext(changes); var held = pendingState(changes); var block = ["# " + built.text].concat(built.extras.map(function (extra) { return "#" + extra; })).join(NEWLINE); pending.close(); queueChange( placeLabel(language, sectionType, $h3), "maneyeke nû hate zêdekirin", function (baseContent) { return { content: insertNewSense(baseContent, language, sectionType, sectionOrdinal, definitionLevel, built.text, built.extras), warnings: [] }; }, $save, $status, // Drawn fresh on every call: redrawPreviews() runs this again whenever anything else // leaves the queue, and the row it drew last time is gone by then. function () { var $pending = $("<li class='dsb-sense-preview dsb-new-sense-pending'></li>"); $li.after($pending); var $edit = $("<span class='dsb-example-toolbar'></span>").append( $("<a href='#' class='dsb-toggle'></a>").attr("title", GLOSS_LABEL) .on("click", function (e) { e.preventDefault(); e.stopPropagation(); pending.reopen(held); })); renderWikitext(block).then(function (html) { if (!$pending.closest("body").length) { return; } var $content = $("<div class='mw-parser-output'></div>") .html(parsedSenseItem(html, 1).html); var $block = $content.children("dl, ul, ol").first(); if ($block.length) { $block.before($edit); } else { $content.append($edit); } $pending.empty().append($content); announceContent($pending); }, function () { $pending.empty().append($("<span></span>").text(built.text), $edit); }); return function () { $pending.remove(); }; }, { key: pending.key, sensePath: definitionLevel, senseLanguage: language } ); } // Both the field save and "Maneyê rake" queue under this, so the later one supersedes the earlier. function senseQueueKey() { return "sense:" + sectionType + "|" + sectionOrdinal + "|" + definitionLevel.join("."); } var $save = $("<button type='button' class='dsb-save'></button>").text(util.msg.add); // Where queueChange() writes its confirmation for actions that have no box of their own. var $note = $("<div class='dsb-status dsb-quick-note'></div>").hide(); var $cancel = $("<button type='button' class='dsb-cancel'></button>").text(util.msg.cancel); var $status = $("<span class='dsb-status'></span>"); var $actions = $("<div class='dsb-form-actions'></div>").append($save, $cancel, $status).hide(); // Emptying the boxes is what says the change has been taken: they were left open with their // text in them, so "Lê zêde bike" could be clicked again and queue the same values twice. function closeOpenBoxes() { $openBoxes.find(".dsb-box-remove").trigger("click"); } $cancel.on("click", function () { if (pending) { pending.close(); if (pending.queued()) { redrawPreviews(); } return; } // Close the boxes too, not just the row. Hiding alone left every field still open with its // text in it, so the next click on the pencil reopened the half-filled panel that had just // been cancelled. Each box's own "x" already clears its getter, restores its link and updates // the counter, so those are triggered rather than repeating the teardown here. $openBoxes.find(".dsb-box-remove").trigger("click"); $note.hide().text(""); // dsb.js owns the pencil's pinned state; tell it rather than hiding behind its back, or the // next click on the pencil would only toggle "pinned" off and appear to do nothing. $quickRow.trigger("dsbCancel"); hideQueueIfIdle(); }); $save.on("click", function () { var changes = collectChanges(); function afterLinkify() { if (pending) { queueNewSense(changes); return; } // This save supersedes any change still queued for the same sense, so it has to write that // one's fields too: the boxes it was made from are closed by now and collectChanges() cannot // see them. var merged = mergeSenseChanges(queuedSenseChanges(), changes); var parts = senseChangeLabels(merged); queueChange( placeLabel(language, sectionType, $h3), parts.length ? parts.join(", ") + (parts.length > 1 ? " hatin guhartin" : " hate guhartin") : "mane hate guhartin", function (baseContent) { return applyAdvancedChanges(baseContent, language, sectionType, sectionOrdinal, definitionLevel, merged); }, $save, $status, function () { return previewSense($li, language, sectionType, sectionOrdinal, definitionLevel); }, // Re-saving this same sense supersedes its pending change rather than queuing another. { key: senseQueueKey(), sensePath: definitionLevel, senseChanges: merged } ); closeOpenBoxes(); } $save.prop("disabled", true); // Auto-linkify the gloss as part of this save, but only a half nobody has linked by hand. // Split back into its two halves first, so the explanation is linked on its own rather than // sitting unseen inside {{îzah|…}}. if (changes.gloss) { $status.removeClass("dsb-status-ok dsb-status-error").text("Tê lînkkirin…"); var glossParts = util.splitTercimeIzah(changes.gloss); linkifyGlossParts( glossParts ? $.trim(glossParts.tercime) : changes.gloss, glossParts ? $.trim(glossParts.izah) : "", function (linkedMane, linkedIzah, error) { // The change is queued either way, so a gloss that came back empty is not written // back -- that would save the sense with its wording gone. var joined = joinGloss(linkedMane, linkedIzah); if ($.trim(joined)) { changes.gloss = joined; } if (error) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text(error); } afterLinkify(); }, true ); } else { afterLinkify(); } }); // The links sit under the buttons, so each open box pushes them down rather than leaving them far above. $quickRow.empty().append($openBoxes, $actions, $picker, $note); if (pending) { // The gloss box is what a new sense is: it opens with the row, and the quotations the row // collects sit above the actions. Done here, once $actions exists. $quickRow.append($pendingQuotes); pending.state.quotes.forEach(function (extra) { $pendingQuotes.append($("<div class='dsb-new-sense-quote'></div>").text("#" + extra)); }); $picker.children(".dsb-field-pick").filter(function () { return $(this).text() === GLOSS_LABEL; }).trigger("click"); } } // Attaches the pencil toggle to one leaf definition <li> -- clicking it reveals the "guhartina pêşketî" // (advanced editing) panel built by buildAdvancedPanel() above. // ---- per-section UI: "+ Beşek lê zêde bike" after a POS block's own content ---- // Reads the header text of "====" subsections already rendered under this POS heading, straight from the DOM. // The subsections of a part of speech are the headings one deeper than it is. function subsectionDepthFor($h3) { return (sections.headingDepth($h3) || 3) + 1; } function isSubsectionHeading($el, depth) { return sections.headingDepth($el) === depth; } function existingSubsectionHeadersInDom($h3) { var depth = subsectionDepthFor($h3); var headers = []; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && here < depth) { break; // out of this part of speech } if (isSubsectionHeading($cursor, depth)) { var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); headers.push(headingText($hh)); } } return headers; } // Finds where a new "====" subsection preview belongs in the live DOM, using the same SUBSECTION_ORDER rank // insertPageSubsection() uses. Returns {insertBefore: $el} (a ranked subsection that outranks this one) or // {insertAfter: $el} (goes at the end of the block). function domSubsectionInsertAnchor($h3, header) { var depth = subsectionDepthFor($h3); var myRank = subsectionRankForHeader(header); var $last = headingOuter($h3); for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && here < depth) { break; // out of this part of speech } if (isSubsectionHeading($cursor, depth)) { var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); var otherRank = subsectionRankForHeader(headingText($hh)); if (otherRank !== -1 && otherRank > myRank) { return { insertBefore: $cursor }; } } $last = $cursor; } return { insertAfter: $last }; } // The wikitext one subsection's content becomes, exactly as insertPageSubsection() writes it -- so the // preview parses the same string the save will produce. function subsectionBodyWikitext(language, s, content) { if (s.kind !== "wordlist") { return content; } return content.filter(function (group) { return group.words.length; }).map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + s.header], "sernav", group.sernav), group.words); }).join("\n\n"); } // The plain word list this preview falls back to when the parse fails: the values that were typed, // without the template around them. Right, but not what the page will look like. function subsectionPlainDom($wrap, s, content) { if (s.kind !== "wordlist") { $wrap.append($("<p class='dsb-preview-text'></p>").text(content)); return; } content.forEach(function (group) { if (group.sernav) { $wrap.append($("<div class='dsb-preview-subheading'></div>").text(group.sernav)); } var $list = $("<ul class='dsb-preview-list'></ul>"); group.words.forEach(function (w) { $list.append($("<li></li>").text(w.replace(/<[^<>]*>/g, ""))); }); $wrap.append($list); }); } // A "not saved yet" preview of a page-level "====" subsection, dropped into the live page at the // position the save will put it, with its body rendered the way the page renders it -- real {{kol3}} // columns and real links, not a list of the words that went into them. Returns the inserted element so // the caller can remove it again. function buildSubsectionPreviewDom($h3, s, content, language) { var $wrap = $("<div class='dsb-preview-block'></div>"); // A styled <div>, not a real <h4>, so existingSubsectionHeadersInDom() never mistakes this live-only // preview for an actual subsection already on the page. $wrap.append($("<div class='dsb-preview-heading' role='heading' aria-level='4'></div>").text(s.header)); $wrap.append($("<span class='dsb-preview-badge'></span>").text("nû — hîn nehatiye qeydkirin")); // Placed before it is filled: a cached render resolves almost at once, and filling a wrap that is // still detached would announce content nobody can see. var anchor = domSubsectionInsertAnchor($h3, s.header); if (anchor.insertBefore) { anchor.insertBefore.before($wrap); } else { anchor.insertAfter.after($wrap); } var wikitext = language ? subsectionBodyWikitext(language, s, content) : ""; if ($.trim(wikitext)) { var $body = $("<div class='dsb-preview-rendered'></div>").appendTo($wrap); fillWithRendered($body, wikitext, function () { $body.remove(); subsectionPlainDom($wrap, s, content); }); } else { subsectionPlainDom($wrap, s, content); } return $wrap; } // A free section owns what lies between its own heading and the next heading of any depth, matching // the range locateHeadingByText() edits. Returns a function that puts the page back, or null. function replaceFreeSectionInPlace($heading, wikitext) { var $start = headingOuter($heading); var body = []; for (var $cursor = $start.next(); $cursor.length; $cursor = $cursor.next()) { if (sections.headingDepth($cursor)) { break; } if (!isOwnFurniture($cursor)) { body.push($cursor.get(0)); } } if (!body.length) { return null; } var $slot = $("<div class='dsb-sense-preview'></div>").insertAfter($start); var $body = $(body).detach(); fillWithRendered($slot, wikitext, function () { $slot.remove(); $slot = null; $body.insertAfter($start); }); return function () { if ($slot) { $body.insertAfter($start); $slot.remove(); $slot = null; } }; } /** * Redraws a subsection that is already on the page with the content this edit would give it. * * The section's own nodes -- everything between its heading and the next heading of the same rank or * higher -- are detached rather than dropped, so the returned function puts the page back exactly as it * was when the change is cancelled or superseded. */ function replaceSubsectionInPlace($h3, header, wikitext) { var depth = subsectionDepthFor($h3); // Walked the same way existingSubsectionHeadersInDom() walks it, so "which nodes are this // subsection" is decided once and the same way everywhere. var $start = null; var body = []; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && here < depth) { break; // out of this part of speech } if (isSubsectionHeading($cursor, depth)) { if ($start) { break; // the next subsection starts here } var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); if (headingText($hh) === header) { $start = $cursor; } continue; } // This gadget's own furniture stays where it is: the subsection's editing panel is the sibling // right after its heading, so sweeping it into the body would detach the very panel the change // was made in and leave the pencil toggling an element that is no longer on the page. if ($start && !isOwnFurniture($cursor)) { body.push($cursor.get(0)); } } if (!$start || !body.length) { return null; } var $slot = $("<div class='dsb-sense-preview'></div>").insertAfter($start); var $body = $(body).detach(); fillWithRendered($slot, wikitext, function () { $slot.remove(); $slot = null; $body.insertAfter($start); }); return function () { if ($slot) { $body.insertAfter($start); $slot.remove(); $slot = null; } }; } /** * The live DOM one "place" section occupies: {nodes, insert}. nodes are what a render replaces -- * empty when the section is not on the page yet -- and insert() puts an element where the section * belongs, so a new one appears exactly where the save will put it. */ // This gadget's own panels sit between a heading and the page's content, so every walk steps over // them rather than counting one as content. function isOwnFurniture($el) { return $el.is(".dsb-toolbar, .dsb-quick-row, .dsb-subsection-panel, .dsb-convert-pos-panel," + " .dsb-preview-block, .dsb-sense-preview, .dsb-preview-inline"); } // A heading's place in the page, which orders queued renames and removals bottom-up. function headingPosition($el) { var $h = $el.is("h2, h3, h4, h5, h6") ? $el : $el.find("h2, h3, h4, h5, h6").first(); return $("h2, h3, h4, h5, h6").index($h); } // One subsection's own nodes on the page, read the way headingSpan() reads the wikitext: a "====" // subsection down to the next heading at its depth or shallower, a free section ($h3 is its heading) // down to the next heading of any depth. hasDeeper says a free section has headings under it. function subsectionDom($h3, s) { var $start = null; var depth; if (s.free) { $start = headingOuter($h3); depth = sections.headingDepth($start) || sections.headingDepth($h3); } else { depth = subsectionDepthFor($h3); for (var $c = headingOuter($h3).next(); $c.length; $c = $c.next()) { var d = sections.headingDepth($c); if (d && d < depth) { break; } if (d === depth) { var $hh = $c.is("h2, h3, h4, h5, h6") ? $c : $c.find("h2, h3, h4, h5, h6").first(); if (headingText($hh) === s.header) { $start = $c; break; } } } } if (!$start) { return null; } var body = []; var hasDeeper = false; for (var $cursor = $start.next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && (s.free || here <= depth)) { hasDeeper = !!s.free && here > depth; break; } if (!isOwnFurniture($cursor)) { body.push($cursor.get(0)); } } return { $heading: $start, $body: $(body), hasDeeper: hasDeeper }; } function loosePlaceDom($h3, subsection) { var place = subsection.place; var nodes = []; var $cursor; if (place === "page-top") { // Şablon:herwiha binêre renders as <div class="rellink binere"> at the head of the content. var $out = $("#mw-content-text .mw-parser-output").first(); var $hat = $out.children(".binere, .rellink").first(); if ($hat.length) { return { nodes: $hat.get(), insert: function ($el) { $hat.before($el); } }; } return { nodes: [], insert: function ($el) { $out.prepend($el); } }; } var $start = headingOuter($h3); if (place === "pron") { // A "===" heading above this POS block, under the language one. for (var $back = $start.prev(); $back.length; $back = $back.prev()) { if ($back.is(".mw-heading2, h2")) { break; } if ($back.is(".mw-heading3, h3")) { var $hh = $back.is("h3") ? $back : $back.find("h3").first(); if (headingText($hh) === subsection.header) { for ($cursor = $back.next(); $cursor.length && !$cursor.is(".mw-heading2, .mw-heading3, h2, h3"); $cursor = $cursor.next()) { if (!isOwnFurniture($cursor)) { nodes.push($cursor.get(0)); } } return { nodes: nodes, insert: function ($el) { $back.after($el); } }; } } } return { nodes: [], insert: function ($el) { $start.before($el); } }; } if (place === "pos-top") { // The thumbnails between the heading and the headword line. for ($cursor = $start.next(); $cursor.length; $cursor = $cursor.next()) { if (isOwnFurniture($cursor)) { continue; } if (!$cursor.is(".thumb, figure, .floatright, .floatleft")) { break; } nodes.push($cursor.get(0)); } return { nodes: nodes, insert: function ($el) { $start.after($el); } }; } return null; } // The wikitext a "place" section's pending content renders as. A word list is its one call; everything // else is already the body that will be written. function loosePlacePreviewWikitext(subsection, content, language) { return subsection.kind === "wordlist" ? wt.buildHbnrCall(loosePlaceWords(content)) : loosePlaceBody(subsection, content, language); } // Draws a section's headword line from wikitext where the page's own line stands, or above the // definitions when the section has none. The page's line is detached rather than dropped, so the // returned function puts it back. function replaceHeadwordInPlace($h3, wikitext) { if (!$.trim(wikitext || "")) { return null; } var $old = $(); var $at = $(); for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("p")) { $old = $cursor; break; } if (sections.headingDepth($cursor) || $cursor.is("ol")) { $at = $cursor; break; } } var $slot = $("<div class='dsb-sense-preview'></div>"); if ($old.length) { $old.before($slot); } else if ($at.length) { $at.before($slot); } else { return null; } var $body = $old.detach(); function restore() { if (!$slot) { return; } $body.insertBefore($slot); $slot.remove(); $slot = null; } fillWithRendered($slot, wikitext, restore); return restore; } // Redraws one of those places where it stands, the same way replaceSubsectionInPlace() redraws a // "====" one. The page's own nodes are detached rather than dropped, so cancelling puts them back. function replaceLoosePlaceInPlace($h3, subsection, wikitext) { if (!$.trim(wikitext || "")) { return null; } var at = loosePlaceDom($h3, subsection); if (!at) { return null; } var $slot = $("<div class='dsb-sense-preview'></div>"); at.insert($slot); var $body = $(at.nodes).detach(); function restore() { if (!$slot) { return; } $body.insertBefore($slot); $slot.remove(); $slot = null; } fillWithRendered($slot, wikitext, restore); return restore; } // Builds the subsection picker + its word-list/text field. A subsection that isn't on the page yet is saved // via insertPageSubsection(); one that already is gets prefilled and saved via updatePageSubsection(). // onlyHeader pins the panel to a single subsection (the per-heading pencil); omit it for the full picker. // free pins the panel to a heading that is not a subsection of a part of speech, located by its own // text: {header, ordinal}. function buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal, onlyHeader, free) { var existing = existingSubsectionHeadersInDom($h3); // A heading the data does not name is still editable. It is declared a word list so the reader // tries, in order, its {{kol3}} calls and then a plain bullet list; anything else falls through to // raw wikitext, which is what prefill() does with a list it cannot read. // Hidden counts as unknown here: the picker does not offer to build a Werger box, but one already // on the page still opens, as the wikitext it is. var unknown = onlyHeader && !PAGE_SUBSECTIONS.some(function (s) { return s.header === onlyHeader && !s.hidden; }); // A free section starts as wikitext: what it holds is only known once it has been read, and // prefill() swaps in the {{kol3}} form there if it turns out to be a list. var available = free ? [{ header: onlyHeader, id: "free", kind: "text", free: free, existing: true }] : unknown ? [{ header: onlyHeader, id: "unknown", kind: "wordlist", unknown: true, existing: true }] : PAGE_SUBSECTIONS.filter(function (s) { // "hidden" takes a section out of the picker without removing it: Werger waits for the // translation tools it belongs to, and offering a box those cannot yet fill misleads. // Werger also carries lang: "ku" -- it is a Kurdish-entry section, so it is not offered // elsewhere either. return !s.hidden && (!s.lang || s.lang === language) && (!onlyHeader || s.header === onlyHeader); }).map(function (s) { return $.extend({}, s, { existing: existing.indexOf(s.header) !== -1 }); }); if (!available.length) { $panel.text("Ji bo vê beşê guhartin tune."); return; } var $select = $("<select class='dsb-subsection-select'></select>"); if (!onlyHeader) { // Label-less on purpose: it holds the "nothing chosen yet" value without becoming a row in the // list (makeSearchable() skips a blank option that has no label), so the prompt lives in the // input's placeholder instead. $select.append($("<option value=''></option>")); } available.forEach(function (s) { // A "place" entry has no "====" heading of its own, so it is marked rather than left looking // like the subsections around it. var label = s.place ? s.header + " (ne binebeş e)" : (s.existing ? s.header + " (biguhêre)" : s.header); $select.append($("<option></option>").val(s.id).text(label)); }); var $selectWrap = makeSearchable($select, "Binebeşek hilbijêre"); if (onlyHeader) { $selectWrap.hide(); // pinned to one subsection: nothing to choose } var $fieldWrap = $("<div class='dsb-subsection-field'></div>"); var $save = $("<button type='button' class='dsb-save'></button>").text(util.msg.add); var $cancel = $("<button type='button' class='dsb-cancel'></button>").text(util.msg.cancel); var $status = $("<span class='dsb-status'></span>"); var $actions = $("<div class='dsb-form-actions'></div>").append($save, $cancel, $status); $cancel.on("click", function () { resetForm(); $panel.hide(); hideQueueIfIdle(); }); // A panel pinned to a heading on the page can rename or remove it. Bilêvkirin and the other places // have no heading of their own to change. var headingEditable = !!onlyHeader && available.length === 1 && available[0].existing && !available[0].place; var $nameWrap = null; // Only a panel pinned to one heading renames it; elsewhere the name is the picked subsection's own. var readName = function (s) { return s.header; }; if (headingEditable) { var subject = available[0]; var $nameLabel = $("<div class='dsb-adv-label'>Nav</div>"); if (subject.free) { var $nameInput = $("<input type='text' class='dsb-input dsb-heading-name'>").val(onlyHeader); readName = function () { return $.trim($nameInput.val()); }; $nameWrap = $("<div class='dsb-heading-name-wrap'></div>").append($nameLabel, $nameInput); } else { // The names the gadget knows, for a subsection of a part of speech; one this part of speech // already has is left out. var $nameSelect = $("<select class='dsb-heading-name'></select>"); var names = [onlyHeader]; PAGE_SUBSECTIONS.forEach(function (s) { if (!s.place && !s.hidden && (!s.lang || s.lang === language) && names.indexOf(s.header) === -1 && existing.indexOf(s.header) === -1) { names.push(s.header); } }); names.forEach(function (name) { $nameSelect.append($("<option></option>").val(name).text(name)); }); var $nameSearch = makeSearchable($nameSelect, "Nav"); $nameSelect.val(onlyHeader).trigger("wiktku:refresh"); readName = function () { return $nameSelect.val() || onlyHeader; }; $nameWrap = $("<div class='dsb-heading-name-wrap'></div>").append($nameLabel, $nameSearch); } var subjectDom = subsectionDom($h3, subject); if (!(subjectDom && subjectDom.hasDeeper)) { var $delete = $("<button type='button' class='dsb-delete'>Beşê jê bibe</button>"); $cancel.after($delete); $delete.on("click", function () { OO.ui.confirm("Beşa «" + subject.header + "» dê were rakirin. Dewam bike?").done(function (ok) { if (!ok) { return; } queueHeadingChange(subject, "beşa «" + subject.header + "» hate rakirin", function (pageContent) { return removeSubsection(pageContent, language, sectionType, sectionOrdinal, subject); }, function () { var dom = subsectionDom($h3, subject); if (!dom) { return null; } var $doomed = dom.$heading.add(dom.$body).addClass("dsb-preview-removed"); return function () { $doomed.removeClass("dsb-preview-removed"); }; }); }); }); } } // Renaming and removing share one queue key, so the later choice replaces the earlier one. Both run // last and bottom-up, since other queued changes find the section by the heading they change. function queueHeadingChange(s, action, transform, previewFn) { var dom = subsectionDom($h3, s); queueChange(subsectionPlaceLabel(s, language, sectionType, $h3), action, function (pageContent) { return { content: transform(pageContent), warnings: [] }; }, $save, $status, previewFn, { runLast: true, key: "heading:" + (s.free ? s.header + "|" + s.free.ordinal : sectionType + "|" + sectionOrdinal + "|" + s.header), pagePosition: dom ? headingPosition(dom.$heading) : 0 }); } function queueRename(s, newName) { queueHeadingChange(s, "beşa «" + s.header + "» bû «" + newName + "»", function (pageContent) { return renameSubsection(pageContent, language, sectionType, sectionOrdinal, s, newName); }, function () { var dom = subsectionDom($h3, s); if (!dom) { return null; } var $note = $("<div class='dsb-preview-inline dsb-preview-heading-note'></div>") .text("→ " + newName + " — nû, hîn nehatiye qeydkirin"); dom.$heading.after($note); return function () { $note.remove(); }; }); } // Undefined while the placeholder option is selected. function currentSubsection() { var id = $select.val(); return id ? available.filter(function (s) { return s.id === id; })[0] : undefined; } // One entry per "{{kol3}}" group in the current wordlist section; empty for any other kind. var groupFields = []; var $groups = null; var editAsText = false; // set when an existing wordlist section turns out to be unparseable // Whatever has been typed into each subsection's box, so switching the picker away and back restores it // instead of rebuilding an empty field. Keyed by subsection id; lastId is whichever box is on screen. var drafts = {}; var lastId = null; // Called before renderField() rebuilds, while the group fields still hold the outgoing box. function captureDraft() { if (!lastId) { return; } drafts[lastId] = imagesField ? { asText: false, images: imagesField.values() } : (groupFields.length ? { asText: false, groups: readGroups() } : { asText: editAsText, text: fieldText() }); } function restoreDraft(s, draft) { // An existing wordlist we previously fell back to raw wikitext for stays in that mode. if (draft.asText && s.kind === "wordlist") { editAsText = true; groupFields = []; renderTextArea(s, 6); } if (imagesField) { imagesField.setValues(draft.images); } else if (groupFields.length) { setGroups(s, draft.groups); } else { setFieldText(draft.text); } } // Etîmolojî is edited on a live surface (see renderChipSurface) rather than in a textarea, so every // read and write of the field goes through these two instead of touching .dsb-subsection-text. var chipSurface = null; function fieldText() { if (wergerForm) { return buildWergerBlock(wergerForm.read()); } return chipSurface ? $.trim(readChipSurface(chipSurface.get(0))) : $.trim($fieldWrap.find(".dsb-subsection-text").val() || ""); } function setFieldText(value) { if (wergerForm) { wergerForm.set(parseWergerBlock(value)); return; } if (chipSurface) { renderChipSurface(chipSurface, value || ""); var schedule = chipSurface.data("dsbSchedulePreview"); if (schedule) { schedule(); // show what the loaded etymology renders as } } else { $fieldWrap.find(".dsb-subsection-text").val(value || ""); } } // The contenteditable surface: text typed directly, template calls as chips that open the dialog, // plus start.js's etymology templates in a searchable insert menu. // A live surface where a template shows as a chip, with a menu that inserts one. Etîmolojî and // Bilêvkirin are both written this way; they differ only in which templates the menu offers. function renderTemplateEditor(s, templates) { $fieldWrap.empty(); var $select = $("<select class='dsb-subsection-select'></select>"); $select.append($("<option value=''></option>")); templates.forEach(function (t) { $select.append($("<option></option>").val(t.snippet).text(t.name + " — " + t.desc)); }); var $surface = $("<div class='dsb-tpl-surface' contenteditable='true'></div>") .attr("role", "textbox") .attr("aria-multiline", "true"); chipSurface = $surface; // Clicking the insert menu moves focus out of the surface and the browser forgets the caret, so // the last caret position inside the surface is kept and restored before inserting -- otherwise // focus() drops it back to the very start and every template lands at the beginning of the line. var savedRange = null; function rememberCaret() { var sel = window.getSelection(); if (!sel || !sel.rangeCount) { return; } var range = sel.getRangeAt(0); var root = $surface.get(0); if (range.commonAncestorContainer === root || $.contains(root, range.commonAncestorContainer)) { savedRange = range.cloneRange(); } } $surface.on("keyup mouseup focusout", rememberCaret); function restoreCaret() { if (!savedRange) { return; } var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(savedRange); } $select.on("change", function () { var snippet = $select.val(); if (!snippet) { return; } // Same substitution start.js makes: CODE is this section's language, SOURCELANG is left for // the user to fill in. snippet = snippet.replace(/CODE/g, language).replace(/SOURCELANG/g, "?"); // A pronunciation template is a bullet of its own ("* {{ku-IPA}}"), so it starts a line // rather than landing in the middle of one. var ownLine = /^\*\s/.test(snippet); if (ownLine) { snippet = $.trim(snippet.replace(/^\*\s*/, "")); } $surface.focus(); restoreCaret(); var $chip = makeTemplateChip(snippet); var piece = document.createDocumentFragment(); if (ownLine) { // The bullet, and a line of its own unless the caret already starts one. var at = caretOffset(); var before = at >= 0 ? readChipSurface($surface.get(0)).slice(0, at) : ""; piece.appendChild(document.createTextNode(!before || /\n$/.test(before) ? "* " : "\n* ")); } piece.appendChild($chip.get(0)); insertAtCaret($surface, piece); rememberCaret(); // so a second insert lands after the first, not back at the old spot $select.val(""); // Straight into the dialog: the snippet is placeholders ("?", "…") until someone fills it in. cite.openTemplateDialog(snippet).then(function (edited) { applyChipEdit($chip, edited); syncPanelWidth(); }); }); // Paste as plain text: pasted markup would otherwise become real nodes in the surface. $surface.on("paste", function (e) { var clip = e.originalEvent && e.originalEvent.clipboardData; if (!clip) { return; } e.preventDefault(); document.execCommand("insertText", false, clip.getData("text/plain")); }); // The chip the dialog was opened for, or the one that has taken its place. renderChipSurface() // rebuilds the surface from its wikitext whenever a call is typed in as text, which throws away // the element this closure is holding; without this, "Jê bibe" would remove a node that is no // longer on screen and appear to do nothing. function liveChip($chip, wikitext) { if ($.contains($surface.get(0), $chip.get(0))) { return $chip; } return $surface.find(".dsb-tpl-chip").filter(function () { return $(this).attr("data-wikitext") === wikitext; }).first(); } function applyChipEdit($chip, edited) { if (edited === null || !$chip.length) { return; } if (!$.trim(edited)) { $chip.remove(); return; } // Edited into something that is no longer one template call: let it fall back to plain text // rather than keeping a chip that lies about its contents. var call = parseTemplateCalls(edited); if (call.otherContent || call.calls.length !== 1) { $chip.replaceWith(document.createTextNode(edited)); return; } $chip.replaceWith(makeTemplateChip(edited)); } // Double-click edits the call's wikitext in place; single click opens the parameter dialog. The // click is held back briefly so the first of a double-click does not open the dialog underneath. var chipClickTimer = null; var lastChipTap = 0; var lastChipNode = null; var DOUBLE_TAP_MS = 300; // Double-tap drops the chip back to plain wikitext in the surface, rather than making the chip // itself editable: a contenteditable inside a contenteditable gives Android a selection handle and // no usable caret. As plain text the browser's own editing works, and chipifyTyped() puts the chip // back once the caret leaves the call. // A collapsed caret inside the text just inserted: at the character tapped where the browser can // say which that is, otherwise just inside the closing braces so the caret stays within the call // (which is what stops chipifyTyped putting the chip straight back). function caretRangeIn(node, event) { var range; var x = event && event.clientX; var y = event && event.clientY; if (x !== undefined && document.caretRangeFromPoint) { range = document.caretRangeFromPoint(x, y); } else if (x !== undefined && document.caretPositionFromPoint) { var pos = document.caretPositionFromPoint(x, y); if (pos) { range = document.createRange(); range.setStart(pos.offsetNode, pos.offset); } } if (range && range.startContainer === node) { range.collapse(true); return range; } var text = node.nodeValue; range = document.createRange(); range.setStart(node, /\}\}$/.test(text) ? text.length - 2 : text.length); range.collapse(true); return range; } function startInlineEdit($chip, event) { if (!$.contains($surface.get(0), $chip.get(0))) { return; // the tap timer and dblclick can both fire; only the first one has a chip left } var node = document.createTextNode($chip.attr("data-wikitext")); $chip.replaceWith(node); $surface.get(0).focus(); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(caretRangeIn(node, event)); } // Bound before the chip's own handler and stopping there, so removing a call never opens the // dialog for it on the way out. $surface.on("click", ".dsb-tpl-chip-remove", function (e) { e.preventDefault(); e.stopImmediatePropagation(); clearTimeout(chipClickTimer); lastChipNode = null; $(this).closest(".dsb-tpl-chip").remove(); syncPanelWidth(); }); $surface.on("click keydown", ".dsb-tpl-chip", function (e) { if (e.type === "keydown" && e.which !== 13 && e.which !== 32) { return; } e.preventDefault(); var $chip = $(this); clearTimeout(chipClickTimer); // A second tap on the same chip edits it inline. Timed here rather than left to "dblclick", // which mobile browsers fire unreliably or not at all for a double-tap. var now = new Date().getTime(); if (lastChipNode === this && now - lastChipTap < DOUBLE_TAP_MS) { lastChipNode = null; startInlineEdit($chip, e); return; } lastChipTap = now; lastChipNode = this; var wikitext = $chip.attr("data-wikitext"); chipClickTimer = setTimeout(function () { cite.openTemplateDialog(wikitext).then(function (edited) { applyChipEdit(liveChip($chip, wikitext), edited); syncPanelWidth(); }); }, DOUBLE_TAP_MS); }); $surface.on("dblclick", ".dsb-tpl-chip", function (e) { e.preventDefault(); clearTimeout(chipClickTimer); startInlineEdit($(this), e); }); // A long etymology cannot be read in the panel's usual 520px column, so the panel drops its // width cap once there is enough text to need it. What it will look like is shown by the // in-place render on the page itself, not by a second copy inside the form. var WIDE_AT = 240; // characters function syncPanelWidth() { var wikitext = readChipSurface($surface.get(0)); if (wikitext.length > WIDE_AT || wikitext.indexOf("\n") !== -1) { $surface.closest(".dsb-subsection-panel").addClass("dsb-panel-wide"); } } // A template typed by hand becomes a chip once typing settles, so it behaves like an inserted one: // click for the parameter dialog, double-click for its raw wikitext. var composing = false; // The caret as an offset into the surface's wikitext, which is what survives rebuilding the DOM. function caretOffset() { var sel = window.getSelection(); var root = $surface.get(0); if (!sel || !sel.rangeCount) { return -1; } var range = sel.getRangeAt(0); if (range.startContainer !== root && !$.contains(root, range.startContainer)) { return -1; // caret is somewhere else on the page } var pre = document.createRange(); pre.selectNodeContents(root); pre.setEnd(range.startContainer, range.startOffset); var holder = document.createElement("div"); holder.appendChild(pre.cloneContents()); return readChipSurface(holder).length; } // Puts the caret back at that wikitext offset. Only runs straight after a rebuild, so the surface // holds nothing but text nodes and chips. function placeCaret(offset) { var root = $surface.get(0); var range = document.createRange(); var nodes = root.childNodes; root.focus(); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var len = node.nodeType === 3 ? node.nodeValue.length : ($(node).attr("data-wikitext") || $(node).text()).length; if (offset <= len) { if (node.nodeType === 3) { range.setStart(node, offset); } else { range.setStartAfter(node); // a chip is atomic; the caret goes after it } break; } offset -= len; } if (!range.startContainer || range.startContainer === document) { range.selectNodeContents(root); } range.collapse(true); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } // True when the selection overlaps the inside of a template call. A caret resting exactly at the // closing "}}" does not count -- that is someone who has just finished typing one. function selectionTouchesCall(wikitext) { var sel = window.getSelection(); if (!sel || !sel.rangeCount) { return false; } var range = sel.getRangeAt(0); var root = $surface.get(0); if (range.startContainer !== root && !$.contains(root, range.startContainer)) { return false; } var from = caretOffset(); var to = from; if (!range.collapsed) { var pre = document.createRange(); pre.selectNodeContents(root); pre.setEnd(range.endContainer, range.endOffset); var holder = document.createElement("div"); holder.appendChild(pre.cloneContents()); to = readChipSurface(holder).length; } return parseTemplateCalls(wikitext).calls.some(function (call) { return from < call.end && to > call.start; }); } function chipifyTyped() { if (composing) { return; // never rebuild under an IME } var wikitext = readChipSurface($surface.get(0)); // Chips already hold one call each, so a higher call count means one was typed in as text. if (parseTemplateCalls(wikitext).calls.length <= $surface.find(".dsb-tpl-chip").length) { return; } var offset = caretOffset(); if (selectionTouchesCall(wikitext)) { return; // still inside a call: leave it as text until the caret moves out } renderChipSurface($surface, wikitext); savedRange = null; // the old range points at nodes that no longer exist if (offset >= 0) { placeCaret(offset); rememberCaret(); } } $surface.on("compositionstart", function () { composing = true; }); $surface.on("compositionend", function () { composing = false; schedulePreview(); }); var previewTimer = null; function schedulePreview() { clearTimeout(previewTimer); previewTimer = setTimeout(function () { chipifyTyped(); syncPanelWidth(); }, 700); } $surface.on("input", schedulePreview); $surface.data("dsbSchedulePreview", schedulePreview); $fieldWrap.append(makeSearchable($select, util.msg.addTemplate), $surface); // Bilêvkirin is written in IPA, so it gets the same character palette the wizard shows. Its // characters go in as text and its two template buttons as chips, which is what the rest of // this surface is made of. if (s.place === "pron") { $fieldWrap.append(util.buildIpaPalette({ insertText: function (text) { $surface.focus(); restoreCaret(); insertAtCaret($surface, document.createTextNode(text)); rememberCaret(); schedulePreview(); }, insertTemplate: function (snippet) { $surface.focus(); restoreCaret(); var $chip = makeTemplateChip(snippet.replace(/\|\s*\}\}$/, "}}")); insertAtCaret($surface, $chip.get(0)); rememberCaret(); cite.openTemplateDialog($chip.attr("data-wikitext")).then(function (edited) { applyChipEdit($chip, edited); syncPanelWidth(); }); } })); } } // The three "place" sections are wikitext with a shape worth spelling out, since nothing on screen // says what belongs in the box. var PLACE_PLACEHOLDERS = { "page-top": "{{h-bnr|Kelîme|Kelîmeyeke din}} an jî {{herwiha binêre/ane}}", "pos-top": "[[Wêne:Navê pelî.jpg|thumb|Binnivîs]]", "pron": "* {{ku-IPA}}" }; function renderTextArea(s, rows) { $fieldWrap.empty().append( $("<textarea class='dsb-input dsb-subsection-text'></textarea>") .attr("rows", rows) .attr("placeholder", PLACE_PLACEHOLDERS[s.place] || (s.header + "…")) ); util.initManualResize($fieldWrap); } // A translation box: the {{werger-ser}}/{{werger-bin}} pair, with the sense it covers and the // layout parameters that template accepts. The rows in between are left to the translation editor // gadget, which is what edits them on the rendered page. // Reads and writes through fieldText()/setFieldText() as wikitext, so drafts, save and the queue // need no special case beyond the append below. var wergerForm = null; var imagesField = null; function renderWergerField(s) { var inputs = {}; // Its own row class rather than .dsb-tpl-param-row: that one is styled in cite.css, which is // only on the page when wikt.cite happens to be loaded, and without it the label falls back to // sitting inline and a short one ("id") is overlapped by its own box. function row(key, label, placeholder) { inputs[key] = $("<input type='text' class='dsb-input'>").attr("placeholder", placeholder || ""); return $("<div class='dsb-werger-row'></div>").append( $("<label class='dsb-adv-label'></label>").text(label).append(inputs[key]) ); } // {{werger-ser}} also takes stûn, firehî, bgcolor, valign and align, but they only restyle the // box and its defaults are what these pages use, so the form does not offer them. They are // still in WERGER_NAMED, so a block that already sets one is read back correctly. var $main = $("<div class='dsb-werger-main'></div>").append( row("1", "Mane", "ava vexwarinê"), row("2", "Mane no", "ne mecbûrî — di parantezê de xuya dike"), row("id", "id", "ne mecbûrî — IDya tercimeyê") ); var $note = $("<p class='dsb-werger-note'></p>").text( "Qutiyeke Wergerê ya vala lê zêde dikî. Tercimeyan bi amûra wergerê dûre binivîse." ); wergerForm = { read: function () { var out = {}; $.each(inputs, function (key, $input) { var value = $.trim($input.val() || ""); if (value) { out[key] = value; } }); return out; }, set: function (params) { $.each(inputs, function (key, $input) { $input.val((params && params[key]) || ""); }); } }; $fieldWrap.empty().append($note, $main); } // Same [[Kategorî:Çavkanî]] search widget start.js's own picker uses: choosing an entry inserts // "* {{Target}}" at the cursor. The field itself stays the plain ".dsb-subsection-text" textarea // every other "kind: text" subsection already uses, so save/prefill/drafts need no changes. function renderCavkaniField(s) { var $ta = $("<textarea class='dsb-input dsb-subsection-text'></textarea>") .attr("rows", 4) .attr("placeholder", util.msg.pickOrTypeTemplate); var picker = cite.buildCategoryTemplateSearch({ placeholder: util.msg.addTemplate, describe: true, // Same param dialog Etîmolojî opens on a fresh insert: filled from the picked template's own // TemplateData, so the user adds "rûpel=", "sernav=", ... before anything lands in the field // rather than typing a bare {{Target}} and going back to fill params in by hand. onPick: function (entry) { cite.openTemplateDialog("{{" + entry.target + "}}").then(function (edited) { if (!edited) { return; // cancelled, or "Jê bibe" on a call that was never inserted } util.insertAtCursor($ta.get(0), "* " + edited + "\n"); $ta.trigger("focus"); }); } }); var $picker = $("<div class='dsb-cavkani-picker'></div>").append(picker.$element); $fieldWrap.empty().append($picker, $ta); } // One "{{kol3}}" group: its "sernav=" heading and its words. Several of these make up one section -- // "Herwiha binêre" typically carries an unheaded list plus a few headed ones. function addGroup(s, group) { var $sernav = $("<input type='text' class='dsb-input'>").attr("placeholder", "ne mecbûrî…"); // {{h-bnr}} takes bare page names, so its rows carry no ⊕: there is nowhere in the call for a // modifier to go. var words = buildWordListField(s.header + "…", { modifiers: !s.single }); var $remove = $("<a href='#' class='dsb-quick-link dsb-group-remove'>Komê rake</a>"); var $element = $("<div class='dsb-wordgroup'></div>").append( // Label beside the box, not above it: stacked, "Sernav" read as a heading for the whole // group rather than as the name of the field under it. The input is nested in the label so // clicking the word focuses the box, without needing an id per group. // A single-call list has no "sernav=" to carry, so the row is built (readGroups still reads // it) but never shown. Hidden here rather than in renderField: setGroups() rebuilds every // group on prefill, which would put a visible one back. $("<div class='dsb-tpl-param-row dsb-param-row-inline'></div>").append( $("<label class='dsb-adv-label'></label>").text("Sernav").append($sernav) ).toggle(!s.single), words.$element, $remove ); var field = { $element: $element, read: function () { return { sernav: $.trim($sernav.val()), words: words.values() }; }, set: function (g) { $sernav.val((g && g.sernav) || ""); words.setValues((g && g.words) || []); } }; $remove.on("click", function (e) { e.preventDefault(); var at = $.inArray(field, groupFields); if (at !== -1) { groupFields.splice(at, 1); } $element.remove(); updateGroupChrome(); }); field.set(group); groupFields.push(field); $groups.append($element); updateGroupChrome(); return field; } // With a single group there is nothing to tell apart, so it needs neither a remove link nor a label. function updateGroupChrome() { var many = groupFields.length > 1; $groups.toggleClass("dsb-wordgroups-many", many); $groups.find(".dsb-group-remove").toggle(many); } function readGroups() { return groupFields.map(function (field) { return field.read(); }); } // What a save writes: the same groups with each word kept once, in the order it first appears. A // list written made bi made often repeats itself, and {{kol3}} only hides that at render time. The // same word under two different headings is two different entries, so groups are deduped apart. function readGroupsForSave() { return readGroups().map(function (group) { var seen = {}; return $.extend({}, group, { // The whole row is the key, modifiers and all, so "dan<t:x>" is not the same word as // "dan". words: (group.words || []).filter(function (word) { var key = $.trim(word); if (seen[key]) { return false; } seen[key] = true; return true; }) }); }); } // Rebuilds the blocks to match what the page (or the draft) holds, always leaving at least one. function setGroups(s, groups) { groupFields = []; $groups.empty(); (groups && groups.length ? groups : [null]).forEach(function (group) { addGroup(s, group); }); } // Loads what the page already has into the form, so an edit starts from the current list rather than // from an empty one. A wordlist section we can't parse degrades to raw wikitext editing. function prefill(s) { $status.removeClass("dsb-status-error dsb-status-ok").text("Tê barkirin…"); $save.prop("disabled", true); fetchPageSubsection(language, sectionType, sectionOrdinal, s).then(function (current) { $save.prop("disabled", false); $status.text(""); if (!current) { return; } // A "place" section has no heading of its own, so whether the page already has one cannot // be seen in the DOM the way a "====" subsection can -- it is known only once its content // has been read. That is what decides "nûkirin" or "zêdekirin" in the edit summary. if (s.place) { s.existing = !!(current.images ? current.images.length : $.trim(current.text || "")); // The label was set before this content arrived, when nothing yet said one was there. $save.text(s.existing && s.kind !== "werger" ? util.msg.edit : util.msg.add); } if (current.kind === "images" && imagesField) { imagesField.setValues(current.images); return; } // A free section's field was built before its content was known: a list turns it into the // {{kol3}} form, anything else leaves it the wikitext box it already is. if (s.free && current.kind === "wordlist" && !groupFields.length) { s.kind = "wordlist"; renderWordListField(s); setGroups(s, current.groups); announceBullets(current); return; } if (current.kind === "wordlist" && groupFields.length) { setGroups(s, current.groups); announceBullets(current); return; } if (s.kind === "wordlist") { editAsText = true; groupFields = []; renderTextArea(s, 6); $status.text(s.unknown ? "Ev beş nayê naskirin — wekî wîkîtekst tê guhartin." : "Şablona vê lîsteyê nehate naskirin — wekî wîkîtekst biguhêre."); } setFieldText(current.text); }, function () { $save.prop("disabled", false); $status.addClass("dsb-status-error").text("Nikare muhtewaya heyî bixwîne."); }).then(function () { if (currentSubsection() === s) { loadedSnapshot = contentSnapshot(); } }); } // A list the page writes made bi made is about to become {{kol3}}, which is a change to the // section even when not one word of it is touched -- so the save is not held back as a no-op. var loadedFromBullets = false; function announceBullets(current) { if (!current.fromBullets) { return; } loadedFromBullets = true; $status.text("Ev lîste made bi made hatiye nivîsîn — bi qeydkirinê dibe {{kol3}}."); } // What the box held when the page's content was put in it, so a save that changed nothing queues // nothing -- renaming a heading does not also rewrite its body. var loadedSnapshot = null; function contentSnapshot() { if (imagesField) { return JSON.stringify(imagesField.values()); } return JSON.stringify(groupFields.length ? readGroups() : fieldText()); } // One "{{kol3}}" group per block, plus a way out to raw wikitext for a list no row can express -- // a shared subpage list like {{herwiha binêre/ane}}, say. function renderWordListField(s) { $fieldWrap.empty(); chipSurface = null; editAsText = false; var $addGroup = $("<a href='#' class='dsb-quick-link'>+ Komek lê zêde bike</a>"); $addGroup.on("click", function (e) { e.preventDefault(); addGroup(s, null); }); $groups = $("<div class='dsb-wordgroups'></div>"); $fieldWrap.append($groups, $addGroup); addGroup(s, null); // One call takes one flat list of names, so there is no second group to add. if (s.single) { $addGroup.hide(); } var $toRaw = $("<a href='#' class='dsb-quick-link'>Wekî wîkîtext biguhêre</a>"); $toRaw.on("click", function (e) { e.preventDefault(); var current = s.place ? wt.buildHbnrCall(loosePlaceWords(readGroupsForSave())) : subsectionBodyWikitext(language, s, readGroupsForSave()); editAsText = true; groupFields = []; $groups = null; renderTextArea(s, 3); setFieldText(current); util.initManualResize($fieldWrap); }); $fieldWrap.append($toRaw); util.initManualResize($fieldWrap); } function renderField() { var s = currentSubsection(); loadedSnapshot = null; loadedFromBullets = false; $fieldWrap.empty(); groupFields = []; $groups = null; editAsText = false; chipSurface = null; wergerForm = null; imagesField = null; $status.removeClass("dsb-status-error dsb-status-ok").text(""); // Save stays disabled (but "Betal bike" usable) until something is actually chosen. $save.prop("disabled", !s); if (!s) { $save.text(util.msg.add); lastId = null; return; // still on "Binebeşekê hilbijêre" } if (s.kind === "wordlist") { renderWordListField(s); } else if (s.kind === "images") { imagesField = util.buildImageListField({ prefix: "dsb-", rowClass: "dsb-tpl-param-row dsb-param-row-inline", inputClass: "dsb-input", addClass: "dsb-quick-link", pageName: mw.config.get("wgTitle") }); $fieldWrap.append(imagesField.$element); } else if (s.header === "Etîmolojî") { renderTemplateEditor(s, templateChoices("etymTpls", language)); } else if (s.place === "pron") { renderTemplateEditor(s, templateChoices("pronTpls", language)); } else if (s.kind === "werger") { renderWergerField(s); } else if (s.header === "Çavkanî") { renderCavkaniField(s); } else { renderTextArea(s, 3); } // Werger always adds a box, even to a section that is already there, so its button never says // "Biguhêre". $save.text(s.existing && s.kind !== "werger" ? util.msg.edit : util.msg.add); // Covers whichever branch above built the field. Idempotent, so a textarea already carrying a // grip is skipped. util.initManualResize($fieldWrap); lastId = s.id; // A draft always wins over re-reading the page: it is the newer, unsaved state. if (drafts[s.id]) { restoreDraft(s, drafts[s.id]); } else if ((s.existing || s.place) && s.kind !== "werger") { prefill(s); } } $select.on("change", function () { captureDraft(); renderField(); }); renderField(); // Back to the state a fresh open would show: no draft, no picked subsection. Called directly // rather than through the "change" handler above, so the discarded draft is never captured first. function resetForm() { drafts = {}; if (!onlyHeader) { $select.val("").trigger("wiktku:refresh"); } renderField(); } $save.on("click", function () { var s = currentSubsection(); if (!s) { return; } var newName = readName(s); if (!newName) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Navê beşê vala ye."); return; } var asText = editAsText || (s.kind !== "wordlist" && s.kind !== "images"); var effective = asText ? $.extend({}, s, { kind: "text" }) : s; var content; var hasContent; if (asText) { content = fieldText(); hasContent = !!content; } else if (s.kind === "images") { content = imagesField ? imagesField.values() : []; hasContent = content.length > 0; } else { content = readGroupsForSave(); hasContent = content.some(function (group) { return group.words.length; }); } var renaming = newName !== s.header; if (renaming) { queueRename(s, newName); } var unchanged = loadedSnapshot !== null && contentSnapshot() === loadedSnapshot && !loadedFromBullets; if (!hasContent || unchanged) { if (unchanged && !renaming) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Tiştek nehatiye guhartin."); } return; } $save.prop("disabled", true); // A Werger section holds any number of boxes, so an existing one is appended to rather than // replaced; every other kind edits the single body it owns. var appending = s.kind === "werger" && s.existing; queueChange( subsectionPlaceLabel(s, language, sectionType, $h3), s.kind === "werger" ? "qutiyeke wergerê hate zêdekirin" : "beşa «" + s.header + "» hate " + (s.existing ? "nûkirin" : "zêdekirin"), function (pageContent) { var body = content; if (appending) { var current = readPageSubsection(pageContent, language, sectionType, sectionOrdinal, effective); var existingText = current ? $.trim(current.text || "") : ""; body = existingText ? existingText + "\n\n" + content : content; } return { content: s.free ? updateFreeSection(pageContent, language, effective, body) : (s.place ? writeLoosePlace(pageContent, language, sectionType, sectionOrdinal, effective, loosePlaceBody(effective, body, language)) : (s.existing ? updatePageSubsection(pageContent, language, sectionType, sectionOrdinal, effective, body) : insertPageSubsection(pageContent, language, sectionType, sectionOrdinal, effective, body))), warnings: [] }; }, $save, $status, // Werger is a box the translation tools fill in afterwards, so there is nothing to draw for // it. A new subsection gets a block of its own at the position it will land in; one already // on the page is redrawn where it stands. s.kind === "werger" ? null : function () { // A free section is redrawn under its own heading, which is the element the panel hangs // on. if (s.free) { var restoreFree = replaceFreeSectionInPlace($h3, subsectionBodyWikitext(language, effective, content)); return function () { if (restoreFree) { restoreFree(); restoreFree = null; } }; } // A "place" section has no heading to redraw between, so it is found in the DOM by what // it renders as and replaced there. if (s.place) { var restoreLoose = replaceLoosePlaceInPlace($h3, effective, loosePlacePreviewWikitext(effective, content, language)); return function () { if (restoreLoose) { restoreLoose(); restoreLoose = null; } }; } if (!s.existing) { var $preview = buildSubsectionPreviewDom($h3, s, content, language); return function () { $preview.remove(); }; } var restore = replaceSubsectionInPlace($h3, effective.header, subsectionBodyWikitext(language, effective, content)); return function () { if (restore) { restore(); restore = null; } }; }, { key: s.free ? "section:" + s.header + "|" + s.free.ordinal : "subsection:" + sectionType + "|" + sectionOrdinal + "|" + s.header } ); }); $panel.empty().append($selectWrap, $nameWrap, $fieldWrap, $actions); } // Builds the "choose a part of speech" picker that reclassifies a section -- an unclassified // "=== Mane ===" placeholder, or a "=== Biwêj ===" that turns out to be a real part of speech. Saved via // convertSectionToPos(); lists every name from sectionNames except the one the section already is. function buildConvertPosPanel($panel, $h3, language, sectionType, sectionOrdinal) { var $select = $("<select class='dsb-convert-pos-select'></select>"); // Label-less, like the subsection picker's: it holds the "nothing chosen yet" value without showing // up as a row, so the prompt lives in the input's placeholder instead. $select.append($("<option value=''></option>")); Object.keys(sectionNames).forEach(function (name) { // The section's own part of speech is listed too, marked: picking it changes nothing about the // heading and leaves the parameters below to be edited on their own. $select.append($("<option></option>").val(name) .text(name === sectionType ? name + " (ya niha)" : name)); }); var $selectWrap = makeSearchable($select, "Li cureyekî rêzimanî bigere"); // Opens on the section's own part of speech, so editing only its parameters needs no pick. if (sectionNames[sectionType]) { $select.val(sectionType).trigger("wiktku:refresh"); } // The same card the entry wizard shows under its part-of-speech question -- gender, transitivity, // the transliteration boxes and everything behind "Tercîhên pêşketî". Shared from // wikt.edit/posparams.js, so reclassifying a section here offers exactly what creating one in the // wizard does. var card = posparams.build({ lang: language, pos: "" }); // Opened on the call the section already has, so this edits the current template rather than // starting from a blank line. setContext() rebuilds the line whenever the part of speech moves, // so the prefill is only the starting point. var spec0 = posTemplateFor(sectionType); card.setContext(language, spec0 ? spec0.name : "", /^Formeke /.test(sectionType)); fetchPageContent().then(function (pageContent) { try { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var found = findPosTemplateLine(loc.lines, loc.posLineIndex, sectionType); if (found.index !== -1 && ($select.val() || sectionType) === sectionType) { card.setWikitext(found.text); } } catch (e) { /* an unreadable section just leaves the card as it was */ } }); $select.on("change", function () { var spec = posTemplateFor($select.val()); card.setContext(language, spec ? spec.name : "", /^Formeke /.test($select.val() || "")); }); // "gh" on its own, or "<form>-<base>" once a structural form is picked -- start.js's own composition. // convertSectionToPos() writes the template itself, so only what follows "{{pos|lang" is wanted // from the card's line. function posParams() { var m = /^\{\{[^|}]*\|[^|}]*\|?([\s\S]*)\}\}$/.exec(card.wikitext()); return m ? m[1] : ""; } var $save = $("<button type='button' class='dsb-save'></button>").text(util.msg.add); var $cancel = $("<button type='button' class='dsb-cancel'></button>").text(util.msg.cancel); var $status = $("<span class='dsb-status'></span>"); var $actions = $("<div class='dsb-form-actions'></div>").append($save, $cancel, $status); // dsb.js owns the pencil and the panel's built state, so it does the closing. $cancel.on("click", function () { $panel.trigger("dsbCancel"); hideQueueIfIdle(); }); $save.on("click", function () { var chosen = $select.val(); if (!chosen) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Pêşî cureyekî rêzimanî hilbijêre."); return; } var params = posParams(); $save.prop("disabled", true); $status.removeClass("dsb-status-error").text(""); queueChange( langLabel(language), "beşa «" + sectionLabel(sectionType, $h3) + "» bû «" + chosen + "»", function (pageContent) { return { content: convertSectionToPos(pageContent, language, sectionType, sectionOrdinal, chosen, params), warnings: [] }; }, $save, $status, function () { // The heading keeps its text, which every other queued change finds the section by; a // note under it names the new one. The headword line is drawn as the save will write it. var $note = $("<div class='dsb-preview-inline dsb-preview-heading-note'></div>") .text("→ " + chosen + (params ? " (" + params + ")" : "") + " — nû, hîn nehatiye qeydkirin"); headingOuter($h3).after($note); var dropped = false; var restoreHeadword = null; fetchPageContent().then(function (pageContent) { if (dropped) { return; } var call; try { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); call = posCallLine(findPosTemplateLine(loc.lines, loc.posLineIndex, sectionType), language, chosen, params); } catch (e) { return; } restoreHeadword = replaceHeadwordInPlace($h3, call); }); return function () { dropped = true; $note.remove(); if (restoreHeadword) { restoreHeadword(); restoreHeadword = null; } }; }, // Runs after every other queued change: it renames the "Mane" heading those changes locate // themselves by, so anything edited on this section has to be applied first. { runLast: true, key: "convert:" + sectionType + "|" + sectionOrdinal, pagePosition: headingPosition($h3) } ); }); $panel.empty().append($selectWrap, card.$element, $actions); } // Attaches the "+ Beşek lê zêde bike" trigger right after a POS block's own last element. // Puts the same pencil the definitions use on every existing "====" subsection heading the gadget knows how // to edit, opening buildSubsectionPanel() pinned to that one subsection. // An editor for [[MediaWiki:Gadget-wikt.translation-adder.js]] whose changes go into this queue, under // "Kurmancî/Navdêr" like every other change to that section. Each change is tried on the page as the // queue would save it before it is queued, as the adder's own editor does. function translationEditor($h3, language, sectionType) { var place = placeLabel(language, sectionType, $h3); var $status = $("<span></span>"); var $save = $("<span></span>"); var adapter = { withCurrentText: function (callback) { fetchPageContent().then(function (content) { callback(queuedContent(content)); }, function () { adapter.error("Nikare muhtewaya rûpelê bixwîne."); }); }, addEdit: function (edit) { adapter.withCurrentText(function (text) { var next; try { next = edit.edit(text); } catch (e) { adapter.error(String(e)); return; } if (typeof next !== "string" || next === text) { return; } queueChange(place, edit.summary, edit.edit, $save, $status, function () { edit.redo(); return edit.undo; }); }); }, error: function (message) { mw.notify(message, { type: "error" }); }, // The adder's "Paşve" takes back its last change. undo: function () { var last = dsbQueue.entries[dsbQueue.entries.length - 1]; if (last) { dropQueueEntry(last); } } }; return adapter; } module.exports = { translationEditor: translationEditor, buildAdvancedPanel: buildAdvancedPanel, buildSubsectionPanel: buildSubsectionPanel, buildConvertPosPanel: buildConvertPosPanel, editExample: editExample, clearQueue: clearQueue, closeAllPanels: closeAllPanels, openQueueBox: openQueueBox }; }()); // </nowiki> 1q1j7za1v1pe6g75vnw36lfpmysu1ym 6794046 6794036 2026-10-14T19:33:53Z Wikihez 7344 DefSideBoxes: translation forms shown on page view; Werger pencil edits existing lines; pending lines highlighted 6794046 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.dsb-panels.js]] * * Everything DefSideBoxes shows once you click: the per-sense advanced panel, the subsection editors, * the part-of-speech picker, the template dialogs, the preview and the queued save. * * Loaded on demand by wikt.dsb, never on a plain page view. It exports only what that half calls. */ (function () { "use strict"; var sections = require("ext.gadget.wikt.core").sections; var edit = require("ext.gadget.wikt.edit"); var cite = require("ext.gadget.wikt.cite"); var util = edit.util; var posparams = edit.posparams; var wt = edit.wt; var startData = edit.data; var langs = edit.langs; var escapeRegExp = util.escapeRegExp; // Local names for the shared wikitext layer, so every call site below reads as it always did. var langHeaderSource = wt.langHeaderSource; var canonicalTemplateKey = wt.canonicalTemplateKey; var aliasPattern = wt.aliasPattern; var splitLeadingCalls = wt.splitLeadingCalls; var splitTemplateParams = wt.splitTemplateParams; var parseTemplateCalls = wt.parseTemplateCalls; var splitCallParams = wt.splitCallParams; var buildTemplateCall = wt.buildTemplateCall; var tdText = wt.tdText; var fetchTemplateData = wt.fetchTemplateData; var parseWordListCall = wt.parseWordListCall; var parseWordListCalls = wt.parseWordListCalls; var namedParam = wt.namedParam; var withNamedParam = wt.withNamedParam; var buildWordListCall = wt.buildWordListCall; var parseBulletWordList = wt.parseBulletWordList; var sectionNames = sections.sectionNames; var MANE_SECTION = sections.MANE_SECTION; var PAGE_SUBSECTIONS = sections.PAGE_SUBSECTIONS; var headingOuter = sections.headingOuter; var headingText = sections.headingText; var isManeHeading = sections.isManeHeading; var sectionTypeOf = sections.sectionTypeOf; var sectionOrdinalWithinLanguage = sections.sectionOrdinalWithinLanguage; var isFormOfWrapper = sections.isFormOfWrapper; var eachLeafDefinition = sections.eachLeafDefinition; var api = new mw.Api(); // metadata[lang].g, minus the grammatical-number codes start.js filters out with isNumberCode(), rendered // the way its genderLabel() does ("m-p" -> "mê (pirjimar)"). // start.js asks transitivity and structural form as two questions and joins them as "<form>-<base>", so // "gerguhêz" alone is "gh" and "xwerû" + "gerguhêz" is "x-gh". // Template call convertSectionToPos() drops under a newly-chosen POS heading, e.g. "Hoker" -> "{{hoker|ku}}". // A "Formeke X" (form-of) heading uses the unchanged base pos template. Doubles as the list of template // calls a conversion may find under the OLD heading and replace, rather than leave stacked beside the new one. // A section's template call. "Formeke Xê" uses X's own template with form=erê. Biwêj is recognised so a // conversion can replace its {{biwêj}}, but never written: no new ones. function posTemplateFor(header) { var form = /^Formeke (.+)ê$/.exec(header || ""); if (form && startData.posChoices[form[1]] && sections.hasFormHeading(form[1])) { return { name: form[1], params: ["form=erê"] }; } var key = util.decapitalize(header || ""); return startData.posChoices[key] ? { name: key, params: [] } : null; } // convertSectionToPos() drops whichever of the tag's names is actually present. var MANE_TAG_REGEX = wt.MANE_TAG_REGEX; // The six per-sense groups start.js writes as "#: {{tpl|lang|word1|word2}}" sub-lines under a "#" definition. // Array order controls where a new group line lands relative to existing ones. Every group lives behind the // quick row's "Hevmane û Bêhtir" dropdown; only the gloss and Nimûne/jêgirtin links sit in the row itself. var SENSE_GROUPS = startData.senseGroups; var GLOSS_LABEL = "Maneyê biguhêre"; var NEWLINE = String.fromCharCode(10); // The column word-list template new sections are written with; the rest of wordlistTemplates are // spellings the parser also accepts. var WORDLIST_TEMPLATE = startData.wordlistTemplates[0]; // ---- {{werger-ser}} / {{werger-bin}}: one translation box ---- // The pair brackets the rows; the translation editor gadget writes the rows themselves onto the // rendered page, so this only ever builds (and reads back) the opening call. // Parameters, from [[Şablon:werger-ser]]: 1 = the sense shown in the box head, 2 = a note in // parentheses after it, id = anchor override (defaults to 1), stûn = column count, firehî = column // width (default 26em), bgcolor, valign (default top), align (default left). var WERGER_NAMED = ["id", "stûn", "firehî", "bgcolor", "valign", "align"]; function buildWergerBlock(params) { var parts = ["werger-ser"]; // Positional slots: 2 can only be reached past 1, so an empty 1 still has to be written. if (params["1"] || params["2"]) { parts.push(params["1"] || ""); } if (params["2"]) { parts.push(params["2"]); } WERGER_NAMED.forEach(function (key) { if (params[key]) { parts.push(key + "=" + params[key]); } }); return "{{" + parts.join("|") + "}}\n{{werger-bin}}"; } // The opening call of the first block in `text`, as the same {1, 2, id, ...} object. Empty object when // there is none, so a half-typed draft restores as an empty form rather than throwing. function parseWergerBlock(text) { var out = {}; var body = wergerSerBody(String(text || "")); if (!body) { return out; } var positional = 0; splitTemplateArgs(body).forEach(function (arg) { var eq = arg.indexOf("="); var name = eq === -1 ? "" : $.trim(arg.slice(0, eq)); if (eq !== -1 && WERGER_NAMED.indexOf(name) !== -1) { out[name] = $.trim(arg.slice(eq + 1)); } else { out[String(++positional)] = $.trim(arg); } }); return out; } // Everything after "{{werger-ser|" up to the "}}" that closes it, or "" when there is no call or it // takes no parameters. Scans for the matching close rather than the first one: a gloss can hold a // template of its own, and a lazy regex would stop inside it. function wergerSerBody(text) { var m = /\{\{\s*werger-ser\s*(?=[|}])/.exec(text); if (!m) { return ""; } var depth = 1; var start = m.index + m[0].length; for (var i = start; i < text.length - 1; i++) { var two = text.substr(i, 2); if (two === "{{") { depth++; i++; } else if (two === "}}") { depth--; i++; if (!depth) { return text.charAt(start) === "|" ? text.slice(start + 1, i - 1) : ""; } } } return ""; } // Splits on "|" at brace/bracket depth 0, so a value containing a template or a link stays whole. function splitTemplateArgs(body) { var args = []; var depth = 0; var current = ""; for (var i = 0; i < body.length; i++) { var two = body.substr(i, 2); if (two === "{{" || two === "[[") { depth++; current += two; i++; continue; } if (two === "}}" || two === "]]") { depth--; current += two; i++; continue; } if (body[i] === "|" && depth <= 0) { args.push(current); current = ""; continue; } current += body[i]; } args.push(current); return args; } // Canonical ku.wiktionary "====" subsection order, used only as insertPageSubsection()'s insertion rank // (see subsectionRankForHeader()). Wider than PAGE_SUBSECTIONS: it also covers subsections the gadget // cannot create but still has to order around. var SUBSECTION_ORDER = startData.subsectionOrder; // ---- searchable <select> ---------------------------------------------------------------------------- // Same widget start.js gets from its util.js wrapNativeSelect(): the native <select> stays as the value // holder (so .val() and "change" handlers keep working) but is hidden behind a text input that filters an // overlay list. Ported rather than required() because util.js is a file inside start.js's package module, // which another gadget cannot reach; the template-row decoration start.js uses is left out. // util.js's wrapNativeSelect(): the native <select> stays the value holder behind a filtering text input. // It reads its own placeholder off data-placeholder, and its wiktku-* styles ship in start.css, so both // gadgets get the identical widget instead of two lookalikes. function makeSearchable($select, placeholder) { $select.attr("data-placeholder", placeholder || "Lê bigere…"); return util.wrapNativeSelect($select); } // ---- small shared utilities ---- // The readable half of an mw.Api rejection. var apiErrorMessage = util.apiErrorMessage; // Thrown from inside an edit() transform to bail out with a specific message. var SubmitAbort = util.SubmitAbort; // A gloss is one field on the page: the sense, then {{îzah|…}} explaining it when there is one. function joinGloss(mane, izah) { return izah ? $.trim(mane + " {{îzah|" + izah + "}}") : $.trim(mane); } // The two halves are linked separately, never joined first: linkify leaves template calls alone by // design, so anything inside {{îzah|…}} would never be looked at. // // A half is skipped when there is nothing in it worth looking up, and -- when onlyUnlinked is set -- // when it already carries a link. Someone who has linked a word by hand has made that half theirs, and // a save must not go on linking the rest of it behind their back; "Oto-lînk bike" leaves the flag off // and still links on demand. function linkifyGlossParts(mane, izah, done, onlyUnlinked) { function needsLinking(text) { if (!$.trim(text) || !util.hasLinkableText(text)) { return false; } return !(onlyUnlinked && text.indexOf("[[") !== -1); } function step(text, next) { if (!needsLinking(text)) { next(text, null); return; } util.linkifyText(text, function (linked, error) { // An empty answer would wipe what the user typed, so only real text is taken. next(typeof linked === "string" && $.trim(linked) ? $.trim(linked) : text, error || null); }); } step(mane, function (linkedMane, maneError) { step(izah, function (linkedIzah, izahError) { done(linkedMane, linkedIzah, maneError || izahError); }); }); } // Runs transformFn(content) inside mw.Api().edit()'s own transform callback so one fetch covers both the // edit-conflict check and the content, preventing a stale overwrite of someone else's edit. // // No badtoken retry here on purpose: edit() posts through postWithEditToken -> postWithToken, which // already throws the cached token away and repeats the request once when the wiki says "badtoken". // A second retry of our own would only double it. function editPage(summary, transformFn, onSuccess, onError) { api.edit(mw.config.get("wgPageName"), function (revision) { return { text: transformFn(revision.content), summary: summary }; }).then(onSuccess, function (codeOrAbort, result) { if (codeOrAbort instanceof SubmitAbort) { onError(codeOrAbort.message); } else { onError(util.msg.saveFailed + apiErrorMessage(codeOrAbort, result)); } }); } // ---- rendered preview: wikitext through the parse API, so a preview shows what the templates // actually produce rather than a plain list standing in for them ---- // Same text is usually asked for twice (open the preview, close it, open it again), so the promise is // kept rather than the call repeated. var renderCache = {}; /** * Renders wikitext as this page would render it. Resolves with an HTML string. * * pst: 1 applies pre-save transform, so ~~~~ and subst: behave as they will once saved. The * modulestyles/modules the parse reports are loaded before resolving: without them TemplateStyles is * missing and the preview would show the right markup in the wrong clothes. */ function renderWikitext(wikitext) { if (renderCache[wikitext]) { return renderCache[wikitext]; } renderCache[wikitext] = api.post({ action: "parse", format: "json", formatversion: 2, title: mw.config.get("wgPageName"), text: wikitext, contentmodel: "wikitext", prop: "text|modules|jsconfigvars", pst: 1, disablelimitreport: 1, disableeditsection: 1, disabletoc: 1 }).then(function (data) { var parsed = (data && data.parse) || {}; if (parsed.modulestyles && parsed.modulestyles.length) { mw.loader.load(parsed.modulestyles); } if (parsed.modules && parsed.modules.length) { mw.loader.load(parsed.modules); } return parsed.text || ""; }, function () { // A failed render must not be remembered, or the next attempt gets the rejection back. delete renderCache[wikitext]; return $.Deferred().reject().promise(); }); return renderCache[wikitext]; } /** * Puts rendered wikitext into $target, replacing whatever placeholder is there. * * The site's own gadgets are told about it through the "wikipage.content" hook, the same way * MediaWiki announces real content, so collapsible lists and the like behave in the preview too. */ // MediaWiki warns when the hook is fired on content that is not in the document, and a gadget that // listens to it can do nothing useful with nodes nobody can see. function announceContent($el) { if ($el.closest("html").length) { mw.hook("wikipage.content").fire($el); } } function fillWithRendered($target, wikitext, onFail) { return renderWikitext(wikitext).then(function (html) { if (!$target.closest("body").length) { return; // the preview was dropped while the parse was in flight } $target.empty().append($("<div class='mw-parser-output'></div>").html(html)); announceContent($target); }, function () { if (onFail) onFail(); }); } /** * Puts a rendered sense in place of the one on the page, keeping this gadget's own furniture (the * pencil's toolbar, the quick row, any open panel) where it is. The page's own nodes are detached * rather than dropped, so the returned function can put them straight back when the change is * cancelled or superseded. * * Nothing labels the result: the change is shown by the sense simply reading the way it will read, * and the queue box is what says nothing is saved yet. */ function replaceSenseInPlace($li, item) { var html = item.html; var $anchor = $li.children(".dsb-toolbar").last(); // class/id/data-* off the parsed <li>, so a queued maneid shows its pill here exactly as it will // once saved. The old values are kept for the restore below. var node = $li.get(0); var before = {}; $.each(node.attributes, function (_, attr) { before[attr.name] = attr.value; }); $.each(item.attrs, function (name, value) { if (name !== "class") { $li.attr(name, value); } }); var addedClasses = (item.attrs["class"] || "").split(/\s+/).filter(function (c) { return c && !$li.hasClass(c); }); $li.addClass(addedClasses.join(" ")); // add-examples hangs its own <ul> off the sense and opens its form as that <ul>'s next sibling. // Detaching those leaves the form to open into a tree nobody can see -- "+ Nimune/jegirtin" takes // the click and shows nothing -- so they stay where they are. Only the rows already in that <ul> // are hidden, since the preview draws the examples itself; the tool's own title line stays. var $keep = $li.children("ul, .add-example-form"); var $hiddenRows = $keep.filter("ul").children("li").filter(function () { return !$(this).find(".add-example-form-title").length; }); $hiddenRows.addClass("dsb-preview-hidden"); var originals = $li.contents().filter(function () { return !$(this).is(".dsb-toolbar, .dsb-quick-row, .dsb-subsection-panel, .dsb-preview-block, .dsb-sense-preview"); }).not($keep).get(); // Where the sense's own nodes sat, so cancelling puts them back there rather than above the quick // row -- the row belongs between the sense and its examples. var backAfter = originals.length ? originals[0].previousSibling : null; var $rendered = $("<span class='dsb-sense-preview'></span>").html(html); if ($anchor.length) { $anchor.after($rendered); } else { $li.prepend($rendered); } $(originals).detach(); announceContent($rendered); // The id pill is drawn by the same code a page load uses, so a queued maneid looks like a saved // one rather than like something this gadget invented. if (window.wikt && wikt.showIDs && wikt.showIDs.process) { wikt.showIDs.process($li); } // dsb.js puts the example pencils back on the redrawn examples. The originals keep theirs. $li.triggerHandler("dsbSenseRedrawn"); return function () { $hiddenRows.removeClass("dsb-preview-hidden"); $li.removeClass(addedClasses.join(" ")); $.each(item.attrs, function (name) { if (name === "class") { return; } if (Object.prototype.hasOwnProperty.call(before, name)) { $li.attr(name, before[name]); } else { $li.removeAttr(name); } }); $li.children(".senseid-displayed, .etymid-displayed, .etymonid-displayed").remove(); if (backAfter && backAfter.parentNode === node) { $(backAfter).after(originals); } else if ($anchor.length) { $anchor.after(originals); } else { $li.prepend(originals); } $rendered.remove(); }; } /** * The <li> out of a parsed "#" block, and the attributes it carries. * * The parse wraps it in its own <ol>, which must not reach the page: the sense already has a number * from the list it lives in. A leading {{maneid}} also makes the parser emit an empty * "li.mw-empty-elt" ahead of the real one, so the first <li> is not necessarily the sense. * * The attributes matter as much as the content: {{maneid|ku|x}} renders as class="senseid" * data-id="x" on the <li> itself, which is what wikt.showids draws its pill from. */ function parsedSenseItem(html, depth) { var $item = $("<div></div>").html(html).find("ol > li").filter(function () { return !$(this).hasClass("mw-empty-elt"); }).first(); // A sub-sense parsed on its own comes back one <ol> deeper per "#": "##" is a list inside a list. // Descend as far as the sense's own depth, or its number would be drawn inside a second list -- // "2." with a "1." under it. for (var level = 1; level < (depth || 1) && $item.length; level++) { var $inner = $item.children("ol").children("li").filter(function () { return !$(this).hasClass("mw-empty-elt"); }).first(); if (!$inner.length) { break; } $item = $inner; } if (!$item.length) { return { html: html, attrs: {} }; } var attrs = {}; $.each($item.get(0).attributes, function (_, attr) { attrs[attr.name] = attr.value; }); return { html: $item.html(), attrs: attrs }; } // This gadget's own save queue + floating widget, separate from the shared Gadget-Editor queue. // Each entry keeps its own transformFn so saveQueueNow() can re-run all of them against one fresh fetch. var dsbQueue = { // [{key, summary, transformFn, runLast, removePreview}]; transformFn: baseContent -> {content, warnings}. // removePreview lives on the entry so replacing one entry takes its own live DOM preview with it. entries: [] }; var $queueBox = null; function dropPreview(entry) { if (typeof entry.removePreview === "function") { try { entry.removePreview(); } catch (e) { /* a preview failing to clean up should never block the queue itself */ } entry.removePreview = null; } } function clearAllPreviews() { dsbQueue.entries.forEach(dropPreview); } function clearQueue() { clearAllPreviews(); dsbQueue.entries = []; if ($queueBox) { $queueBox.$list.empty(); $queueBox.$box.hide(); } } // Closes every editor UI on the page. Each open box's own "x" clears its getter, restores its picker // link and updates the counter, so those are triggered rather than repeating that work here; hiding a // row alone would leave its fields populated and the next pencil click would reopen them half-filled. function closeAllPanels() { $(".dsb-open-boxes .dsb-box-remove").trigger("click"); $(".dsb-quick-note").hide().text(""); $(".dsb-more-menu").hide(); $(".dsb-subsection-panel, .dsb-convert-pos-panel").hide(); // dsb.js owns each pencil's pinned state; tell it rather than hiding behind its back, or the next // click on the pencil would only toggle "pinned" off and appear to do nothing. $(".dsb-quick-row").trigger("dsbCancel"); } function ensureQueueBox() { if ($queueBox) { return $queueBox; } var $box = $("<div class='dsb-queue-box'></div>").hide(); var $close = $("<a href='#' class='dsb-queue-close' title='Veşêre'>&#215;</a>"); var $title = $("<div class='dsb-queue-title'></div>").text("Guhartinên li benda qeydkirinê"); var $list = $("<ul class='dsb-queue-list'></ul>"); var $empty = $("<div class='dsb-queue-empty'></div>").text( "Guhartinên te dê li vir werin rêzkirin. Heta ku tu pêl «Guhartinan qeyd bike» nekî, " + "tiştek li ser rûpelê nayê weşandin." ); var $status = $("<div class='dsb-queue-status'></div>"); var $save = $("<button type='button' class='dsb-queue-save'>Guhartinan qeyd bike</button>"); var $cancel = $("<button type='button' class='dsb-queue-cancel'></button>").text(util.msg.cancel); var $actions = $("<div class='dsb-queue-actions'></div>").append($save, $cancel); $close.on("click", function (e) { e.preventDefault(); $box.hide(); }); $cancel.on("click", function () { clearQueue(); closeAllPanels(); }); $save.on("click", function () { saveQueueNow(); }); $box.append($close, $title, $empty, $list, $actions, $status); $(document.body).append($box); $queueBox = { $box: $box, $list: $list, $empty: $empty, $status: $status, $save: $save, $cancel: $cancel }; return $queueBox; } // Takes one change out of the queue and puts the page back as it was before it, its in-place preview // included. The queue is the only record of what "Guhartinan qeyd bike" will write, so every line in it // has to be removable on its own -- closing the panel that made a change does not unmake it. // Each preview draws the page the whole queue would save, so one entry leaving changes what all the // others should show. They are taken down and drawn again from what is left. function redrawPreviews() { dsbQueue.entries.forEach(function (entry) { if (typeof entry.previewFn !== "function") { return; } dropPreview(entry); try { var removePreview = entry.previewFn(); if (typeof removePreview === "function") { entry.removePreview = removePreview; } } catch (e) { /* a broken preview never stops the queue */ } }); } function dropQueueEntry(entry) { var at = dsbQueue.entries.indexOf(entry); if (at === -1) { return; } dropPreview(entry); dsbQueue.entries.splice(at, 1); redrawPreviews(); renderQueueBox(); hideQueueIfIdle(); } function renderQueueBox() { var box = ensureQueueBox(); box.$list.empty(); dsbQueue.entries.forEach(function (entry) { var $drop = $("<button type='button' class='dsb-queue-drop'>&#215;</button>") .attr("title", "Vê guhartinê ji rêzê derxe"); $drop.on("click", function () { dropQueueEntry(entry); }); box.$list.append($("<li></li>").text(entry.summary).append($drop)); }); box.$empty.toggle(!dsbQueue.entries.length); box.$status.removeClass("dsb-status-ok dsb-status-error").text(""); box.$save.prop("disabled", !dsbQueue.entries.length); box.$box.show(); } // Shown the moment a panel opens, empty, so it is clear before anything is typed that a change waits // for "Guhartinan qeyd bike" rather than publishing itself. function openQueueBox() { renderQueueBox(); } // Nothing queued and no form left open means nothing to save, so the box goes away rather than // lingering on an empty state. function hideQueueIfIdle() { if (dsbQueue.entries.length || !$queueBox) { return; } if ($(".dsb-quick-row:visible, .dsb-subsection-panel:visible, .dsb-convert-pos-panel:visible").length) { return; } $queueBox.$box.hide(); } // Asks core's postEdit module to show its toast on the next load (dsb.js loads it there), then reloads. // Loaded here rather than declared as a dependency, so a reload happens even if it fails. function reloadWithPostEditNotice() { mw.loader.using("mediawiki.action.view.postEdit").then(function (require) { require("mediawiki.action.view.postEdit").fireHookOnPageReload("saved"); }).always(function () { location.reload(); }); } // A transform returns either the new page text or {content, warnings}; both shapes are accepted. // One that returns neither stops the save, named, rather than writing a broken page. function transformedContent(entry, pageContent) { var result = entry.transformFn(pageContent); var next = typeof result === "string" ? result : (result && result.content); if (typeof next !== "string") { throw new SubmitAbort("Guhartin nehate sepandin: " + entry.summary); } return next; } // Queue order, except that runLast entries move to the end: converting "=== Mane ===" into a real POS // heading renames the very heading every other queued change locates itself by. // Those run from the bottom of the page up: a rename changes the ordinals of the sections after it // ("Mane 2" becomes "Mane 1"), never of the ones before. // Two sense paths in page order; index 0 of a definitionLevel is unused. function compareSensePath(a, b) { var len = Math.max(a.length, b.length); for (var i = 1; i < len; i++) { var x = a[i] === undefined ? -1 : a[i]; var y = b[i] === undefined ? -1 : b[i]; if (x !== y) { return x - y; } } return 0; } function orderedEntries() { var normal = []; var last = []; dsbQueue.entries.forEach(function (entry) { (entry.runLast ? last : normal).push(entry); }); last.sort(function (a, b) { return b.pagePosition - a.pagePosition; }); // A sense is found by its number, so removing or adding one moves every sense below it. The sense // changes therefore run bottom-up, each in the slot it was queued in, and every one of them still // finds the line it was queued for. var slots = []; normal.forEach(function (entry, i) { if (entry.sensePath) { slots.push(i); } }); var senses = slots.map(function (i) { return normal[i]; }); senses.sort(function (a, b) { return compareSensePath(b.sensePath, a.sensePath); }); slots.forEach(function (slot, k) { normal[slot] = senses[k]; }); return normal.concat(last); } /** * The pending page as previews and example lookups need it: every queued transform, in save order, * except the runLast section renames. Those callers find their section under the heading it has on * the page now, which a queued "Mane" -> "Navdêr" would take away, and a rename leaves the senses as * they are. An entry that cannot apply is skipped -- the save reports that properly, and a preview * must not shout. */ function queuedContent(pageContent) { var content = pageContent; orderedEntries().filter(function (entry) { return !entry.runLast; }).forEach(function (entry) { try { content = transformedContent(entry, content); } catch (e) { // Left out of the preview; the save reports it properly. Named, so a change that fails to // draw is distinguishable from one with nothing to draw. mw.log.warn("wikt.dsb: pêşdîtin bêyî vê guhartinê hate xêzkirin — " + entry.summary, e); } }); return content; } /** * Where a sense sits in the page the queue would save. A queued removal of an earlier sense moves * everything below it up one, while the path was read off the page before that. Returns null when the * sense itself, or a sense it hangs under, is the one being removed. */ function queuedSensePath(language, definitionLevel) { var path = definitionLevel.slice(); var gone = false; dsbQueue.entries.forEach(function (entry) { var other = entry.removesSense && entry.sensePath; if (!other || entry.senseLanguage !== language || other[0] !== path[0]) { return; } var depth = other.length - 1; if (depth >= path.length) { return; // a sub-sense of something else; it moves nothing here } for (var i = 1; i < depth; i++) { if (other[i] !== path[i]) { return; // another branch of the list } } if (other[depth] < path[depth]) { path[depth] -= 1; } else if (other[depth] === path[depth]) { gone = true; } }); return gone ? null : path; } /** * Redraws one sense where it sits, from the page the queue would save -- so a queued gloss edit, a * queued example and everything else pending on that sense are shown together, rendered by the parser * rather than described in a box. Returns the queue's own "take the preview down" function. * * One preview per sense: a second queued change takes the first drawing down and draws the queue. */ function previewSense($li, language, sectionType, sectionOrdinal, definitionLevel) { var cancelled = false; var restore = null; function undo() { cancelled = true; if (restore) { restore(); restore = null; } if ($li.data("dsbPreview") === undo) { $li.removeData("dsbPreview"); } } var previous = $li.data("dsbPreview"); if (typeof previous === "function") { previous(); } $li.data("dsbPreview", undo); fetchPageContent().then(function (pageContent) { if (cancelled) { return; } var path = queuedSensePath(language, definitionLevel); if (!path) { return; } var wikitext; try { wikitext = senseBlockWikitext(queuedContent(pageContent), language, sectionType, sectionOrdinal, path); } catch (e) { return; } renderWikitext(wikitext).then(function (html) { if (cancelled || $li.data("dsbPreview") !== undo || !$li.closest("body").length) { return; } restore = replaceSenseInPlace($li, parsedSenseItem(html, definitionLevel.length - 1)); }); }); return undo; } // The real save: re-runs every queued transformFn, in order, against one freshly-fetched revision and // posts the result as a single edit with a combined summary. function saveQueueNow() { if (!dsbQueue.entries.length) { return; } var box = ensureQueueBox(); box.$save.prop("disabled", true); box.$cancel.prop("disabled", true); box.$status.removeClass("dsb-status-ok dsb-status-error").text("Tê qeydkirin…"); var entries = orderedEntries(); // Grouped by place, in the order the places were first queued: several changes to one section read // as "Kurmancî/Navdêr 2: a, b, c" rather than repeating the section name in front of each of them. var order = []; var byPlace = {}; entries.forEach(function (entry) { if (!Object.prototype.hasOwnProperty.call(byPlace, entry.place)) { byPlace[entry.place] = []; order.push(entry.place); } byPlace[entry.place].push(entry.action); }); var combinedSummary = order.map(function (place) { var actions = byPlace[place].join(", "); return place ? place + ": " + actions : actions; }).join("; ") + " (DefSideBoxes)"; editPage( combinedSummary, function (pageContent) { var content = pageContent; entries.forEach(function (entry) { content = transformedContent(entry, content); }); return content; }, function (data) { forgetPageContent(); clearAllPreviews(); dsbQueue.entries = []; box.$list.empty(); box.$status .removeClass("dsb-status-error") .addClass("dsb-status-ok") .text("✓ Hemû guhartin hatin qeydkirin."); box.$save.prop("disabled", true); box.$cancel.prop("disabled", false); // A real edit reloads the page and gets MediaWiki's own "Guhartina te hat weşandin." toast, the // one a normal save shows. A null edit changed nothing, so the page stays as it is. if (data && data.newrevid) { reloadWithPostEditNotice(); } }, function (message) { box.$save.prop("disabled", false); box.$cancel.prop("disabled", false); box.$status.removeClass("dsb-status-ok").addClass("dsb-status-error").text(message); } ); } // "Kurmancî/Navdêr" -- where a queued change lands, for its summary. The language name comes from the // shipped list; an unknown code is left as the code itself. // "Kurmancî/Navdêr", or "Kurmancî/Navdêr 2" where the language has more than one section of that // type. Without the number a summary on a page with two Navdêr sections named neither of them. // Numbered only when it disambiguates: on the usual page "Navdêr 1" would just be noise. function placeLabel(language, sectionType, $h3) { return langLabel(language) + "/" + sectionLabel(sectionType, $h3); } // Bilêvkirin sits above the parts of speech and belongs to the whole language, so naming the section // it was opened from would say something untrue about what the edit touches. function subsectionPlaceLabel(subsection, language, sectionType, $h3) { if (subsection && subsection.free) { return langLabel(language) + "/" + subsection.header; } return subsection && subsection.place === "pron" ? langLabel(language) : placeLabel(language, sectionType, $h3); } function sectionLabel(sectionType, $h3) { if ($h3 && $h3.length && sections.sectionTypeCountWithinLanguage($h3) > 1) { return sectionType + " " + sections.sectionOrdinalWithinLanguage($h3); } return sectionType; } // Capitalised because it opens the summary; Kurdish otherwise writes language names lower-case. function langLabel(language) { var names = langs.names(); return util.capitalize(names[language] || language); } // The parts of a sense the form actually changed, so the summary lists them instead of saying nothing. // A save on a sense supersedes the one queued for it (they share a key), so it has to write that one's // fields too. newer over older: a field the newer save touched wins, the rest stays as the older save // left it. A gloss is only ever present when its box was open, so a missing one means untouched. function mergeSenseChanges(older, newer) { if (!older) { return newer; } var merged = { gloss: newer.gloss || older.gloss, maneid: newer.changed.maneid ? newer.maneid : older.maneid, etiket: newer.changed.etiket ? newer.etiket : older.etiket, kategori: newer.changed.kategori ? newer.kategori : older.kategori, words: {}, changed: $.extend({}, older.changed, newer.changed) }; SENSE_GROUPS.forEach(function (group) { merged.words[group.key] = newer.changed["word:" + group.key] ? newer.words[group.key] : (older.words[group.key] || []); }); return merged; } // The sense's fields as the page has them, with a queued change written over the top: what the boxes // prefill from and what collectChanges() diffs against, so reopening a box shows the value that is // waiting to be saved rather than the one it is about to replace. function overlaySenseFields(fields, queued) { if (!queued) { return fields; } var out = { gloss: queued.gloss || fields.gloss, maneid: queued.changed.maneid ? queued.maneid : fields.maneid, etiket: queued.changed.etiket ? queued.etiket : fields.etiket, kategori: queued.changed.kategori ? queued.kategori : fields.kategori, words: {} }; SENSE_GROUPS.forEach(function (group) { out.words[group.key] = queued.changed["word:" + group.key] ? queued.words[group.key] : fields.words[group.key]; }); return out; } function senseChangeLabels(changes) { var parts = []; var changed = changes.changed || {}; if (changes.gloss) { parts.push("mane"); } if (changes.newSense) { parts.push("maneyeke nû"); } SENSE_GROUPS.forEach(function (group) { if (changed["word:" + group.key]) { parts.push(group.label.toLowerCase()); } }); if (changed.maneid) { parts.push("id"); } if (changed.etiket) { parts.push("etîket"); } if (changed.kategori) { parts.push("kategorî"); } return parts; } // Queues transformFn(baseWikitext) -> {content, warnings} for the real save to run later against fresh // content (see saveQueueNow()); nothing here touches the page yet. Also runs previewFn(), which drops a // small "not saved yet" preview into the live DOM at the change's own spot on the page. Shared by every // panel that queues a wikitext change: buildAdvancedPanel, buildSubsectionPanel, buildConvertPosPanel. // place is what the change is being made to ("Kurmancî/Navdêr 2"), action is what is being done to it. // Kept apart rather than pre-joined so saveQueueNow() can name a section once and list everything // queued against it after it, instead of repeating the place before every item. function queueChange(place, action, transformFn, $save, $status, previewFn, opts) { opts = opts || {}; var entry = { key: opts.key || null, place: place || "", action: action, summary: place ? place + ": " + action : action, transformFn: transformFn, runLast: !!opts.runLast, pagePosition: opts.pagePosition || 0, // Which sense the change belongs to, so orderedEntries() can apply the senses bottom-up. sensePath: opts.sensePath || null, // The language section that path is counted in, and whether this change deletes the sense. senseLanguage: opts.senseLanguage || null, removesSense: !!opts.removesSense, // The sense fields this entry writes, merged with whatever an earlier entry under the same // key wrote, so superseding it does not drop what it had already collected. senseChanges: opts.senseChanges || null, // Kept, not just run: redrawPreviews() calls it again when another entry leaves the queue. previewFn: typeof previewFn === "function" ? previewFn : null, removePreview: null }; // Queuing the same target twice replaces the pending change rather than stacking a second one -- // two "+Têkildar" entries would both run, and the second would abort on "Ev beş jixwe heye". var superseded = false; if (entry.key) { for (var i = dsbQueue.entries.length - 1; i >= 0; i--) { if (dsbQueue.entries[i].key === entry.key) { dropPreview(dsbQueue.entries[i]); dsbQueue.entries.splice(i, 1); superseded = true; } } } dsbQueue.entries.push(entry); $status.removeClass("dsb-status-error").addClass("dsb-status-ok").text( "✓ li rêza guhartinan hate zêdekirin — pêl «Guhartinan qeyd bike» bike da ku bi cih bibe." ); $save.prop("disabled", false); if (previewFn) { try { var removePreview = previewFn(); if (typeof removePreview === "function") { entry.removePreview = removePreview; } } catch (e) { /* a broken preview should never stop the change from being queued */ } } // What the superseded entry drew is gone from the queue, so whatever else is drawn from it is stale. // A queued removal renumbers the senses under it, so every other preview is stale the same way. if (superseded || entry.removesSense) { dsbQueue.entries.forEach(function (other) { if (other !== entry) { dropPreview(other); } }); redrawPreviews(); } renderQueueBox(); } // Runs text through the ext.gadget.linkify gadget ($.kuLinkify) and calls back with the linkified result. // Passes the text through unchanged if the gadget can't be loaded, so it never blocks a save. var linkifyText = util.linkifyText; // ---- wikitext location ---- // // One copy for both editing gadgets, in wikt.edit: add-examples writes into the same places. var langSectionRange = wt.langSectionRange; var locateSection = wt.locateSection; var findDefinitionLine = wt.findDefinitionLine; /** * The wikitext of one sense's own block: its "#" line plus everything nested under it -- the "#:" word * lines, the "#*" citations and any sub-senses -- stopping at the next line of the same depth that * starts a new sense. Parsed on its own that block renders as exactly the <li> the page shows. */ function senseBlockWikitext(pageContent, language, sectionType, sectionOrdinal, definitionLevel) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var depth = definitionLevel.length - 1; var out = [lines[defLine]]; for (var i = defLine + 1; i < lines.length; i++) { var m = /^(#+)([:*]?)/.exec(lines[i]); if (!m || m[1].length < depth) { break; } // Same depth and no ":" or "*" after the hashes is the next sense, not part of this one. if (m[1].length === depth && !m[2]) { break; } out.push(lines[i]); } return out.join("\n"); } // Inserts or extends a "#: {{tplKey|lang|word1|word2}}" sub-line for one sense (start.js's wordLine() // format). If a line for this tplKey already exists under the definition, missing words are appended to // it; otherwise a new line lands after the last existing group line, always before any "#*" citation lines. function insertSenseWords(pageContent, language, sectionType, sectionOrdinal, definitionLevel, tplKey, words, opts) { opts = opts || {}; var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = new Array(definitionLevel.length).join("#"); // definitionLevel.length - 1 "#" characters // Group 1 is the spelling the page uses, group 2 the parameters: an existing {{hevwate}} stays // {{hevwate}} rather than being silently renamed to {{hevmane}}. // ".*" rather than "[^}]*": a list holding a nested call ("{{hevmane|ku|{{g|ku|x}}}}") stopped // matching at the first "}", and an unrecognized line meant a second one was written beside it. var tplLineRegex = new RegExp("^" + marker + ":\\s*\\{\\{\\s*(" + aliasPattern(tplKey) + ")\\s*\\|(.*)\\}\\}\\s*$"); var groupKeysPattern = SENSE_GROUPS.map(function (g) { return aliasPattern(g.key); }).join("|"); var groupLineRegex = new RegExp("^" + marker + ":\\s*\\{\\{\\s*(" + groupKeysPattern + ")\\s*\\|"); var anyBlockLineRegex = new RegExp("^" + marker + "[:*]"); var lastGroupLine = -1; for (var i = defLine + 1; i < lines.length; i++) { if (!anyBlockLineRegex.test(lines[i])) { break; } var tplMatch = tplLineRegex.exec(lines[i]); if (tplMatch) { var foundKey = tplMatch[1]; if (opts.replace) { // Editing an existing list: an empty result means the user cleared it, so drop the line. if (words.length) { lines[i] = marker + ": {{" + foundKey + "|" + language + "|" + words.join("|") + "}}"; } else { lines.splice(i, 1); } return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } var params = tplMatch[2].split("|"); // [lang, word1, word2, ...] var existingWords = params.slice(1); words.forEach(function (w) { if (existingWords.indexOf(w) === -1) { existingWords.push(w); } }); lines[i] = marker + ": {{" + foundKey + "|" + language + "|" + existingWords.join("|") + "}}"; return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } if (groupLineRegex.test(lines[i])) { lastGroupLine = i; } } // Nothing to write and no line to edit: a call with no words is a module error ("Parametreya 2 // lazim e"), so an empty list leaves the page alone rather than writing one. if (!words.length) { return pageContent; } var insertAt = lastGroupLine >= 0 ? lastGroupLine + 1 : defLine + 1; lines.splice(insertAt, 0, marker + ": {{" + tplKey + "|" + language + "|" + words.join("|") + "}}"); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Canonical left-to-right order of the small templates that can prefix a definition line's gloss text: // maneid, then kategori (K), then etîket (e). var LEADING_TPL_ORDER = startData.leadingTplOrder; // True for a leading call the advanced panel edits in a box of its own -- those are kept out of the // gloss box so the same template is never editable in two places at once. Redirect spellings count: // {{f}} and {{etîket}} are {{e}}. function hasOwnFieldBox(key) { return LEADING_TPL_ORDER.indexOf(canonicalTemplateKey(key)) !== -1; } // Inserts, extends, or (with opts.replace) overwrites one of the small templates that prefix a definition // line's gloss text -- {{maneid|lang|id}}, {{K|lang|kat1|...}} or {{e|lang|etîket1|...}}. Under // opts.replace an empty values list removes the call rather than writing an empty one. function insertLeadingSenseTpl(pageContent, language, sectionType, sectionOrdinal, definitionLevel, tplKey, values, opts) { opts = opts || {}; var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var afterMarker = lines[defLine].slice(marker.length).replace(/^\s+/, ""); // Collecting the whole leading-call run means an existing tplKey call is found and merged wherever it // already sits, not just when it happens to be first. var split = splitLeadingCalls(afterMarker); var calls = split.calls; var rest = split.rest; var existing = null; calls.forEach(function (c) { if (canonicalTemplateKey(c.key) === tplKey) { existing = c; } }); if (existing) { if (opts.replace) { // Editing an existing call: an empty result means the user cleared it, so drop the call. if (values.length) { existing.params = [language].concat(values); } else { calls = calls.filter(function (c) { return c !== existing; }); } } else { var existingVals = existing.params.slice(1); values.forEach(function (v) { if (existingVals.indexOf(v) === -1) { existingVals.push(v); } }); existing.params = [language].concat(existingVals); } } else if (!values.length) { return pageContent; // nothing to add, and nothing there to clear } else { // Not present yet -- insert right before the first existing call that outranks tplKey in // LEADING_TPL_ORDER (an unrecognized call always outranks everything), or at the end of the run. var myRank = LEADING_TPL_ORDER.indexOf(tplKey); var insertAt = calls.length; for (var i = 0; i < calls.length; i++) { var otherRank = LEADING_TPL_ORDER.indexOf(canonicalTemplateKey(calls[i].key)); if (otherRank === -1 || otherRank > myRank) { insertAt = i; break; } } calls.splice(insertAt, 0, { key: tplKey, params: [language].concat(values) }); } var rebuilt = calls.map(function (c) { return "{{" + c.key + "|" + c.params.join("|") + "}}"; }).join(" "); lines[defLine] = marker + " " + (rebuilt ? rebuilt + " " : "") + rest; return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Replaces just the gloss text of a definition line, leaving any leading template calls untouched -- // the save half of "Maneyê biguhêre" (edit sense). function updateSenseGloss(pageContent, language, sectionType, sectionOrdinal, definitionLevel, newGloss) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var afterMarker = lines[defLine].slice(marker.length).replace(/^\s+/, ""); // The box holds everything except the calls with their own field, so those are put back in front. // Dropping "{{mane?}}" here is what clears start.js's placeholder once a real gloss is written. var kept = splitLeadingCalls(afterMarker).calls.filter(function (c) { return hasOwnFieldBox(c.key); }); var leadingText = kept.map(function (c) { return "{{" + c.key + "|" + c.params.join("|") + "}}"; }).join(" "); lines[defLine] = marker + " " + (leadingText ? leadingText + " " : "") + newGloss; return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Deletes one sense: its definition line plus everything indented under it (its "#:" groups, "#*" // citations, any sub-senses). Refuses to empty a section, since a POS heading with no senses is worse // than the sense the user wanted gone. // A new sense goes directly below the one whose pencil asked for it -- after the lines that sense owns // (its "#:" examples, "#*" citations and "##" sub-senses), so it reads as the next sense rather than // landing at the foot of the section. It takes the same marker depth as its neighbour, so asking from // a "##" sub-sense adds a sibling sub-sense. // extras are the sense's own "#:" lines, written under it in one go: a sense that is not on the // page yet cannot be located by number, so everything it carries goes in with the insertion. function insertNewSense(pageContent, language, sectionType, sectionOrdinal, definitionLevel, text, extras) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var end = defLine + 1; var ownedRegex = new RegExp("^" + marker + "[#:*]"); while (end < lines.length && ownedRegex.test(lines[end])) { end++; } var block = [marker + " " + text]; (extras || []).forEach(function (extra) { block.push(marker + extra); }); lines.splice.apply(lines, [end, 0].concat(block)); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } function removeSense(pageContent, language, sectionType, sectionOrdinal, definitionLevel) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var marker = /^#+/.exec(lines[defLine])[0]; var end = defLine + 1; var ownedRegex = new RegExp("^" + marker + "[#:*]"); while (end < lines.length && ownedRegex.test(lines[end])) { end++; } // Top-level senses of this section only -- "##" sub-senses go with their parent either way. if (definitionLevel.length === 2) { var siblings = 0; for (var i = loc.posLineIndex + 1; i < lines.length && !/^==/.test(lines[i]); i++) { if (/^#[^*#:]/.test(lines[i])) { siblings++; } } if (siblings < 2) { throw new SubmitAbort("Ev maneya dawî ya vê beşê ye. Ji bo rakirinê divê tu tevahiya beşê rakî."); } } lines.splice(defLine, end - defLine); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Fetches the page's current wikitext and pulls out just this sense's gloss text to prefill the "Maneyê // biguhêre" form. A plain read, not part of the hardened save path -- the actual save still re-fetches // fresh content, so only the prefilled text itself could be a little stale. // Current contents of one existing subsection, for prefilling the edit form. // This page's current wikitext. // One fetch for the whole queue: every preview reads the same revision, and asking per preview meant // downloading the page again for each queued change. Dropped after a save, when the page has moved on. var pageContentPromise = null; function forgetPageContent() { pageContentPromise = null; } function fetchPageContent() { if (pageContentPromise) { return pageContentPromise; } pageContentPromise = rawPageContent(); // A failed read must not be remembered as the answer. pageContentPromise.fail(forgetPageContent); return pageContentPromise; } function rawPageContent() { return api.get({ action: "query", prop: "revisions", rvprop: "content", titles: mw.config.get("wgPageName"), formatversion: 2 }).then(function (data) { var page = data.query && data.query.pages && data.query.pages[0]; var content = page && !page.missing && page.revisions && page.revisions[0] && page.revisions[0].content; if (!content) { throw new SubmitAbort("Nikare muhtewaya rûpelê bixwîne."); } return content; }); } function fetchPageSubsection(language, sectionType, sectionOrdinal, subsection) { return fetchPageContent().then(function (content) { return readPageSubsection(content, language, sectionType, sectionOrdinal, subsection); }); } function fetchSenseFields(language, sectionType, sectionOrdinal, definitionLevel) { return fetchPageContent().then(function (content) { return readSenseFields(content, language, sectionType, sectionOrdinal, definitionLevel); }); } // Everything the advanced panel can edit about one sense, read off its definition line and the "#:" lines // under it: the raw gloss (templates included), the leading maneid/K/e values, and each word-list group. function readSenseFields(pageContent, language, sectionType, sectionOrdinal, definitionLevel) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var defLine = findDefinitionLine(lines, loc.posLineIndex, definitionLevel); var defMarker = /^#+/.exec(lines[defLine])[0]; var afterMarker = lines[defLine].slice(defMarker.length).replace(/^\s+/, ""); var leading = {}; splitLeadingCalls(afterMarker).calls.forEach(function (c) { leading[canonicalTemplateKey(c.key)] = c.params.slice(1); }); // Only start.js's "{{mane?|lang}}" placeholder is hidden; every other template stays visible so it can // be edited by hand in the gloss box. // The gloss box shows the line minus the calls that have their own box ({{maneid}}, {{K}}, {{e}} and // their redirects) and minus start.js's "{{mane?}}" placeholder. Everything else -- a {{g}} link, a // {{guharto}} -- stays visible and editable there. var split = splitLeadingCalls(afterMarker); var glossCalls = split.calls.filter(function (c) { return !hasOwnFieldBox(c.key) && canonicalTemplateKey(c.key) !== "mane?"; }); var glossLead = glossCalls.map(function (c) { return "{{" + c.key + "|" + c.params.join("|") + "}}"; }).join(" "); var gloss = $.trim((glossLead ? glossLead + " " : "") + split.rest); var marker = new Array(definitionLevel.length).join("#"); var groupKeysPattern = SENSE_GROUPS.map(function (g) { return aliasPattern(g.key); }).join("|"); var groupLineRegex = new RegExp("^" + marker + ":\\s*\\{\\{\\s*(" + groupKeysPattern + ")\\s*\\|([^}]*)\\}\\}\\s*$"); var anyBlockLineRegex = new RegExp("^" + marker + "[:*]"); var words = {}; for (var i = defLine + 1; i < lines.length; i++) { if (!anyBlockLineRegex.test(lines[i])) { break; } var m = groupLineRegex.exec(lines[i]); if (m) { words[canonicalTemplateKey(m[1])] = m[2].split("|").slice(1); } } return { gloss: gloss, maneid: leading.maneid ? leading.maneid[0] : "", etiket: leading.e || [], kategori: leading.K || [], words: words }; } // Finds a POS block's body range (up to, but not including, the next heading of level <= 3), and the // header text + line index of any "====" subsections it already contains. // depth is the part-of-speech heading's own ("===" on most entries, "====" where the senses are // grouped under numbered etymologies). Its block runs to the next heading at that depth or shallower, // and its subsections are the headings one deeper. function posBlockBounds(lines, posLineIndex, depth) { depth = depth || 3; var blockEnd = wt.headingAtOrAbove(depth); var subsectionHeading = wt.headingAtDepth(depth + 1); var end = lines.length; var existingHeaders = []; for (var i = posLineIndex + 1; i < lines.length; i++) { if (blockEnd.test(lines[i])) { end = i; break; } var sub = subsectionHeading.exec(lines[i]); if (sub) { existingHeaders.push({ header: $.trim(sub[1]), lineIndex: i }); } } return { end: end, existingHeaders: existingHeaders, depth: depth }; } // Looks up a header's rank in SUBSECTION_ORDER. -1 for a header that isn't one of ours; such a header is // never used as an insertion anchor. function subsectionRankForHeader(header) { return SUBSECTION_ORDER.indexOf(header); } // Body line range of one "====" subsection inside a POS block: everything after its heading up to the next // "====" heading, or the end of the block. Null if the block has no such subsection. function subsectionBodyRange(bounds, header) { var headers = bounds.existingHeaders; for (var i = 0; i < headers.length; i++) { if (headers[i].header === header) { return { start: headers[i].lineIndex + 1, end: i + 1 < headers.length ? headers[i + 1].lineIndex : bounds.end }; } } return null; } // ---- template-parameter editing ------------------------------------------------------------------- // A gloss that is nothing but one template call gets a labelled box per parameter instead of a raw // wikitext line, the way VisualEditor's template dialog works. // The single template call a gloss is made of, or null when the line is anything less tidy. {{g|...}} is a // link inside the gloss rather than the gloss itself, so it neither disqualifies the line nor gets a form. function glossTemplateTarget(gloss) { var parsed = parseTemplateCalls(gloss || ""); if (parsed.otherContent) { return null; } var editable = parsed.calls.filter(function (c) { return canonicalTemplateKey(c.name) !== "g"; }); return editable.length === 1 ? editable[0] : null; } // ---- the three places that carry no "====" heading of their own ---------------------------------- // Bilêvkirin is a "===" section above the POS blocks, images sit at the top of a POS block, and the // h-bnr hatnote goes above the first language heading. Each is located here and then read, written or // inserted through the same three lines of splice, so nothing below needs to know about them. var IMAGE_LINE = /^\[\[\s*(?:Wêne|File|Image|Dosya)\s*:/i; /** * {lines, langSectionIndex, exists, start, end, insertAt} for a subsection carrying a "place", or null * for an ordinary one. start..end is the body as it stands; insertAt is where a missing one goes. */ function locateLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection) { var place = subsection.place; if (!place) { return null; } var i, end; if (place === "page-top") { // Above every language section, so the whole page is the search space. The call is found under // any of its names, and its own name is carried back so a save never renames it. var pageLines = pageContent.split("\n"); var call = wt.findHbnrCall(pageContent); if (call) { return { lines: pageLines, langSectionIndex: 0, exists: true, call: call, start: call.lineIndex, end: call.lineIndex + 1, insertAt: call.lineIndex }; } return { lines: pageLines, langSectionIndex: 0, exists: false, start: 0, end: 0, insertAt: 0 }; } var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; if (place === "pron") { // One per language, not one per part of speech, so it is looked for above the POS heading. for (i = 0; i < loc.posLineIndex; i++) { var h3 = /^===\s*([^=]+?)\s*===/.exec(lines[i]); if (h3 && $.trim(h3[1]) === subsection.header) { end = i + 1; while (end < lines.length && !/^==/.test(lines[end])) { end++; } return { lines: lines, langSectionIndex: loc.langSectionIndex, exists: true, start: i + 1, end: end, insertAt: i }; } } // Straight after the language heading, which puts it before every POS block. return { lines: lines, langSectionIndex: loc.langSectionIndex, exists: false, start: 1, end: 1, insertAt: 1 }; } if (place === "pos-top") { // Between the conjugation line and the headword template -- where the wizard writes them. var at = loc.posLineIndex + 1; while (at < lines.length && /^\{\{\s*ku-tewîn/.test($.trim(lines[at]))) { at++; } var from = at; while (at < lines.length && IMAGE_LINE.test($.trim(lines[at]))) { at++; } return { lines: lines, langSectionIndex: loc.langSectionIndex, exists: at > from, start: from, end: at, insertAt: from }; } return null; } // The wikitext a "place" section's content becomes. A word list there is one template call, not a // {{kol3}} block, so the words are joined into it -- and any ⊕ modifier a row picked up is dropped, // since a page name is all the call can carry. function loosePlaceBody(subsection, content, language) { if (subsection.kind === "images") { return $.grep($.map(content || [], function (image) { return wt.buildImageLine({ file: image.file, caption: wt.captionForLang(image.caption, language), alt: image.alt, size: image.size, extra: image.extra }); }), Boolean).join("\n"); } return content; } // The words a "place" word list holds, flattened out of its single group. Any ⊕ modifier a row picked // up is dropped: a page name is all the call can carry. function loosePlaceWords(content) { var words = []; $.each(content || [], function (_, group) { $.each(group.words || [], function (_, raw) { var plain = $.trim(util.parseWordValue(raw).word); if (plain && $.inArray(plain, words) === -1) { words.push(plain); } }); }); return words; } // Writes one of those places, inserting it when it is not there yet and replacing it when it is. Only // Bilêvkirin carries a heading; the other two are bare lines. function writeLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection, content) { var at = locateLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection); if (!at) { throw new SubmitAbort("Cih nehate dîtin: " + subsection.header); } // A call naming a subpage -- "{{h-bnr/-ane}}" -- holds a list shared by every page transcluding // it. Writing here would change this page alone and desync the rest, so the edit is refused and // the template that actually owns the list is named instead. if (at.call && at.call.subpage) { throw new SubmitAbort("Ev rûpel «{{" + at.call.name + "}}» bi kar tîne — lîste li " + "[[Şablon:" + at.call.subpage + "]] ye, lê wê zêde bike, ne li vê rûpelê."); } // A word list here is one template call, so it is built where the existing call's own name is // known -- editing "{{bnr-h|...}}" must not turn it into "{{h-bnr|...}}". var body = subsection.kind === "wordlist" ? wt.buildHbnrCall(loosePlaceWords(content), at.call && at.call.name) : $.trim(content || ""); // A "===" section is separated from what is above and below it by a blank line, the way the rest // of an entry is written. The other two places are bare lines inside a block and take none. var heading = subsection.place === "pron"; function blank(index) { return $.trim(at.lines[index] || "") === ""; } if (at.exists) { if (body) { var replacement = [body]; // end is the next "==" heading; the blank line before it is inside the range being replaced. if (heading && /^==/.test(at.lines[at.end] || "")) { replacement.push(""); } at.lines.splice.apply(at.lines, [at.start, at.end - at.start].concat(replacement)); } else { // Emptying the box removes the section rather than leaving an empty heading behind. at.lines.splice(at.start, at.end - at.start); if (heading) { at.lines.splice(at.insertAt, 1); // The blank line that separated it goes with it, rather than doubling up. if (blank(at.insertAt) && blank(at.insertAt - 1)) { at.lines.splice(at.insertAt, 1); } } } } else { if (!body) { throw new SubmitAbort("Tiştek nehate nivîsandin."); } var block = heading ? ["=== " + subsection.header + " ===", body] : [body]; if (heading) { if (!blank(at.insertAt - 1)) { block.unshift(""); } if (!blank(at.insertAt)) { block.push(""); } } at.lines.splice.apply(at.lines, [at.insertAt, 0].concat(block)); } return pageContent.slice(0, at.langSectionIndex) + at.lines.join("\n"); } // What a section body holds: its {{kol3}} groups, the plain bullet list that stands in for them, or // the raw wikitext when it is neither. A free section is read this way before its kind is known. function readSubsectionBody(body, subsection) { if (subsection.kind === "wordlist" || subsection.free) { var calls = parseWordListCalls(body); if (calls.length) { return { kind: "wordlist", groups: calls.map(function (call) { return { sernav: namedParam(call.named, "sernav"), words: call.words }; }) }; } var bullets = parseBulletWordList(body); if (bullets) { // Editable as a word list; saving rewrites the bullets as {{kol3}}. return { kind: "wordlist", groups: [{ sernav: "", words: bullets }], fromBullets: true }; } } return { kind: "text", text: $.trim(body) }; } // The wikitext a section body becomes after an edit. A wordlist keeps its own template name, language // and named params ("cure=Jê", ...) and only has its positional words replaced; a text one is // replaced whole. function rewriteSubsectionBody(body, language, subsection, content) { if (subsection.kind !== "wordlist") { // Replacing the body wholesale drops that separator, so put it back. return content + (/\n\s*$/.test(body) ? "\n" : ""); } var calls = parseWordListCalls(body); if (calls.length) { // Each call is rewritten where it stands, so text between and around them survives untouched. // Last to first, so the offsets of the earlier ones stay valid as we go. var replacement = body; for (var gi = calls.length - 1; gi >= 0; gi--) { var call = calls[gi]; var group = content[gi]; // A group emptied in the form drops its call entirely rather than leaving "{{kol3|ku}}". var rebuilt = group && group.words.length ? buildWordListCall(call.name, call.lang, withNamedParam(call.named, "sernav", group.sernav), group.words) : ""; replacement = replacement.slice(0, call.start) + rebuilt + replacement.slice(call.end); } // Groups the form added beyond the ones already there go after the last of them. var added = content.slice(calls.length).filter(function (group) { return group.words.length; }).map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + subsection.header], "sernav", group.sernav), group.words); }); if (added.length) { replacement = replacement.replace(/\s*$/, "") + "\n\n" + added.join("\n\n") + "\n"; } return replacement.replace(/\n{3,}/g, "\n\n"); // gaps left by any dropped call } if (parseBulletWordList(body)) { // A plain bullet list is replaced wholesale by a real {{kol3}} on its first edit. return content.map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + subsection.header], "sernav", group.sernav), group.words); }).join("\n\n") + (/\n\s*$/.test(body) ? "\n" : ""); } throw new SubmitAbort("Nikare lîsteya vê beşê bixwîne: " + subsection.header); } // ---- sections that are not subsections of a part of speech ---- // A "=== Etîmolojî ===" that the parts of speech hang under, a "=== Çavkanî ===" at the foot of a // language, a heading nobody planned for. Located by its own text, and edited as the wikitext under // it -- or as a {{kol3}} list when that is what it holds. function freeSectionBody(pageContent, language, subsection) { var loc = wt.locateHeadingByText(pageContent, language, subsection.header, subsection.free.ordinal); return { loc: loc, body: loc.lines.slice(loc.bodyStart, loc.bodyEnd).join("\n") }; } function readFreeSection(pageContent, language, subsection) { var at = freeSectionBody(pageContent, language, subsection); return readSubsectionBody(at.body, subsection); } function updateFreeSection(pageContent, language, subsection, content) { var at = freeSectionBody(pageContent, language, subsection); at.loc.lines.splice(at.loc.bodyStart, at.loc.bodyEnd - at.loc.bodyStart, rewriteSubsectionBody(at.body, language, subsection, content)); return pageContent.slice(0, at.loc.langSectionIndex) + at.loc.lines.join("\n"); } // ---- removing and renaming a heading ---- // A subsection's heading line and the end of what it owns, as {loc, heading, end, depth, bounds}. A // "====" subsection runs to the next one, taking deeper headings with it. A free section owns only its // own text: what sits under it belongs to the parts of speech (see removeSubsection). function headingSpan(pageContent, language, sectionType, sectionOrdinal, subsection) { if (subsection.free) { var free = wt.locateHeadingByText(pageContent, language, subsection.header, subsection.free.ordinal); return { loc: free, heading: free.headingLineIndex, end: free.bodyEnd, depth: free.depth }; } var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var bounds = posBlockBounds(loc.lines, loc.posLineIndex, loc.depth); var range = subsectionBodyRange(bounds, subsection.header); if (!range) { throw new SubmitAbort(util.msg.sectionMissing + subsection.header); } return { loc: loc, heading: range.start - 1, end: range.end, depth: bounds.depth + 1, bounds: bounds }; } // A free section with deeper headings under it is refused: removing it would leave them under the // section before, and taking them along would remove whole parts of speech. function hasDeeperHeadingAfter(lines, index, depth) { var m = /^(=+)[^=]/.exec(lines[index] || ""); return !!m && m[1].length > depth; } function removeSubsection(pageContent, language, sectionType, sectionOrdinal, subsection) { var span = headingSpan(pageContent, language, sectionType, sectionOrdinal, subsection); var lines = span.loc.lines; if (subsection.free && hasDeeperHeadingAfter(lines, span.end, span.depth)) { throw new SubmitAbort("Di bin «" + subsection.header + "» de beşên din hene; pêşî wan jê bibe."); } lines.splice(span.heading, span.end - span.heading); // The blank line that separated it goes with it, rather than doubling up. if (span.heading > 0 && !$.trim(lines[span.heading - 1]) && !$.trim(lines[span.heading] || "x")) { lines.splice(span.heading, 1); } return pageContent.slice(0, span.loc.langSectionIndex) + lines.join("\n"); } function renameSubsection(pageContent, language, sectionType, sectionOrdinal, subsection, newHeader) { var span = headingSpan(pageContent, language, sectionType, sectionOrdinal, subsection); var lines = span.loc.lines; if (span.bounds && span.bounds.existingHeaders.some(function (h) { return h.header === newHeader; })) { throw new SubmitAbort("Ev beş jixwe heye: " + newHeader); } var marks = wt.headingMarks(span.depth); lines[span.heading] = marks + " " + newHeader + " " + marks; // A {{kol3}} names its section in cure=, which follows the heading. Read across lines: a call is // often written one parameter per line. var cure = new RegExp("(\\|\\s*cure\\s*=\\s*)" + escapeRegExp(subsection.header) + "(\\s*[|}])", "g"); var body = lines.slice(span.heading + 1, span.end).join("\n").replace(cure, "$1" + newHeader + "$2"); lines.splice.apply(lines, [span.heading + 1, span.end - span.heading - 1].concat(body.split("\n"))); return pageContent.slice(0, span.loc.langSectionIndex) + lines.join("\n"); } // Current contents of an existing "====" subsection. A wordlist one whose template can't be parsed comes // back as {kind: "text"} so the caller can still edit it as raw wikitext instead of refusing outright. function readPageSubsection(pageContent, language, sectionType, sectionOrdinal, subsection) { if (subsection.free) { return readFreeSection(pageContent, language, subsection); } var loose = locateLoosePlace(pageContent, language, sectionType, sectionOrdinal, subsection); if (loose) { if (!loose.exists) { return null; } if (loose.call) { return { kind: "wordlist", groups: [{ sernav: "", words: loose.call.words }] }; } var looseBody = $.trim(loose.lines.slice(loose.start, loose.end).join("\n")); if (subsection.kind === "images") { return { kind: "images", images: $.grep($.map(looseBody.split("\n"), wt.parseImageLine), Boolean) }; } if (subsection.kind !== "wordlist") { return { kind: "text", text: looseBody }; } // One template call rather than a {{kol3}} block: its positional parameters are the words. var looseCall = parseTemplateCalls(looseBody).calls[0]; return { kind: "wordlist", groups: [{ sernav: "", words: (looseCall && looseCall.params) || [] }] }; } var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var bounds = posBlockBounds(loc.lines, loc.posLineIndex, loc.depth); var range = subsectionBodyRange(bounds, subsection.header); if (!range) { return null; } return readSubsectionBody(loc.lines.slice(range.start, range.end).join("\n"), subsection); } // Rewrites an existing "====" subsection in place. A wordlist one keeps its own template name, language and // named params ("cure=Jê", ...) and only has its positional words replaced; a text one is replaced whole. function updatePageSubsection(pageContent, language, sectionType, sectionOrdinal, subsection, content) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var bounds = posBlockBounds(lines, loc.posLineIndex, loc.depth); var range = subsectionBodyRange(bounds, subsection.header); if (!range) { throw new SubmitAbort(util.msg.sectionMissing + subsection.header); } var body = lines.slice(range.start, range.end).join("\n"); lines.splice(range.start, range.end - range.start, rewriteSubsectionBody(body, language, subsection, content)); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // Inserts a new "==== Header ====" subsection into its SUBSECTION_ORDER position within a POS block, right // before the first ranked subsection that outranks it, or at the end if none does. Either a // {{kol3|...}} word list or a plain paragraph, matching start.js's own subsection formats. function insertPageSubsection(pageContent, language, sectionType, sectionOrdinal, subsection, content) { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var bounds = posBlockBounds(lines, loc.posLineIndex, loc.depth); var alreadyThere = false; bounds.existingHeaders.forEach(function (h) { if (h.header === subsection.header) { alreadyThere = true; } }); if (alreadyThere) { throw new SubmitAbort("Ev beş jixwe heye: " + subsection.header); } var body = subsection.kind === "wordlist" ? content.map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + subsection.header], "sernav", group.sernav), group.words); }).join("\n\n") : content; var myRank = subsectionRankForHeader(subsection.header); var headers = bounds.existingHeaders; var insertAt = bounds.end; for (var i = 0; i < headers.length; i++) { var otherRank = subsectionRankForHeader(headers[i].header); if (otherRank !== -1 && otherRank > myRank) { insertAt = headers[i].lineIndex; break; } } // Exactly one blank line on each side: the line before the insertion point is normally already blank // (it separated the previous subsection from the next heading), so only add one where there isn't one. var block = []; if (insertAt > 0 && $.trim(lines[insertAt - 1]) !== "") { block.push(""); } var marks = wt.headingMarks(bounds.depth + 1); block.push(marks + " " + subsection.header + " " + marks, body); if (insertAt < lines.length) { block.push(""); } lines.splice.apply(lines, [insertAt, 0].concat(block)); return pageContent.slice(0, loc.langSectionIndex) + lines.join("\n"); } // ---- converting an unclassified "=== Mane ===" placeholder heading into a real POS heading ---- // Some pages carry a heading literally named "Mane" (optionally "Mane 2" etc.) instead of a real // part-of-speech name, tagged right below with {{cure?|<lang>}} (ku.wiktionary's "part of speech not yet // determined" maintenance flag). It is a section key of its own (sections.MANE_SECTION), so the shared // locator finds it like any other heading; only the rewriting below is particular to it. // Renames the "=== Mane ===" placeholder to the chosen POS heading, drops the maintenance tag line right // after it if present (MANE_TAG_REGEX -- cure? or any of its known aliases), and inserts the matching // "{{<pos>|<lang>}}" call in its place (skipped when the section has no template of its own). // Other content under the heading is left untouched. /** * Where a section's own headword template sits, as {index, text}, or {index: -1} when it has none. * * Not always the line under the heading: a "{{ku-tewîn-lk}}" line and any number of "[[Wêne:...]]" * images can come first. Everything down to the first definition line is searched, and index doubles * as the slot a new call belongs in when there is no old one. */ function findPosTemplateLine(lines, headingLine, sectionType) { var oldSpec = posTemplateFor(sectionType); var oldTemplate = oldSpec ? oldSpec.name : (sectionType === "Biwêj" ? "biwêj" : null); var oldTemplateRegex = oldTemplate && new RegExp("^\\{\\{\\s*" + escapeRegExp(oldTemplate) + "\\s*[|}]"); var at = headingLine + 1; while (at < lines.length && !/^#/.test(lines[at]) && !/^==/.test(lines[at])) { if (MANE_TAG_REGEX.test(lines[at]) || (oldTemplateRegex && oldTemplateRegex.test(lines[at]))) { return { index: at, text: lines[at], end: at }; } at++; } return { index: -1, text: "", end: at }; } // Named parameters merged in order, each written once: a later value replaces an earlier one of the // same name. Positional values are kept as they come. function mergeCallParams() { var out = []; var seen = {}; $.each(Array.prototype.slice.call(arguments), function (_, list) { $.each(list || [], function (__, param) { var m = /^\s*([^=|]+?)\s*=([\s\S]*)$/.exec(param); if (!m) { out.push(param); return; } // "z" and "c" are one name to Modul:rêziman, so the form's "c=m" meets the page's "z=". var key = wt.canonicalParamName(m[1]); if (!Object.prototype.hasOwnProperty.call(seen, key)) { seen[key] = out.length; out.push(param); return; } // The page's own spelling stays when it carried a value; an empty one gives way entirely. var earlier = /^\s*([^=|]+?)\s*=([\s\S]*)$/.exec(out[seen[key]]); // "?" asks for a value, so it never replaces one the page already has. if ($.trim(earlier[2]) && $.trim(m[2]) === "?") { return; } out[seen[key]] =$.trim(earlier[2]) ? earlier[1] + "=" + m[2] : param; }); }); return out; } // What the call on the page already says, minus its language. Carried over only when the new section // uses that same template -- "z=m" survives Navdêr becoming Formeke navdêrê, while a verb's "c=h-gh" // has no meaning on a noun and is left behind. function keptCallParams(line, templateName) { var call = parseTemplateCalls($.trim(line || "")).calls[0]; if (!call || canonicalTemplateKey(call.name) !== canonicalTemplateKey(templateName)) { return []; } return call.params.slice(1).filter(function (param) { return /=/.test(param); }); } // The headword call a converted section gets, or "" when the new section has none. What the old call // (found, from findPosTemplateLine) said keeps its place, then the section's own defaults, then what the // form was filled in with -- each name written once, the last value winning, so a converted call reads // as small a change from the old one as it can. function posCallLine(found, language, newSectionType, posParams) { var spec = posTemplateFor(newSectionType); if (!spec) { return ""; } var kept = found.index !== -1 ? keptCallParams(found.text, spec.name) : []; // A lemma is not an inflected form, so the old section's form=erê stays behind. if (spec.params.indexOf("form=erê") === -1) { kept = kept.filter(function (param) { return !/^\s*form\s*=\s*erê\s*$/.test(param); }); } var params = mergeCallParams(kept, spec.params, posParams ? posParams.split("|") : []); return "{{" + spec.name + "|" + language + (params.length ? "|" + params.join("|") : "") + "}}"; } function convertSectionToPos(pageContent, language, sectionType, sectionOrdinal, newSectionType, posParams) { if (!sectionNames[newSectionType]) { throw new SubmitAbort("Navê cureyê rêzimanî yê hilbijartî nediyar e."); } // locateSection() already handles the "Mane" placeholder as a section type of its own. var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var lines = loc.lines; var headingLine = loc.posLineIndex; var depth = loc.depth; // A section straight under a numbered group heading takes that heading's place: the group heading // held nothing but its gloss. var group = depth > 3 ? wt.numberedGroupAbove(lines, headingLine, depth) : -1; if (group !== -1) { lines.splice(group, headingLine - group); headingLine = group; depth--; } // At the heading's own depth. Written without a number: the renumbering pass below decides every // number in this language section at once, so one carried over from the old name would just be // overwritten. var marks = wt.headingMarks(depth); lines[headingLine] = marks + " " + newSectionType + " " + marks; // The line under the heading is either the old section's own template call ("{{biwêj|ku}}") or, for a // Mane placeholder, the "needs a POS" maintenance tag. Both are replaced by the new POS's call. // The new call takes the old one's own place -- or, when there was none, the slot just above the // definitions, which is where the wizard writes it. var found = findPosTemplateLine(lines, headingLine, sectionType); var insertAt = found.index !== -1 ? found.index : found.end; if (found.index !== -1) { lines.splice(found.index, 1); } var call = posCallLine(found, language, newSectionType, posParams); if (call) { lines.splice(insertAt, 0, call); } // Renaming a section can make it repeat one the page already had, and can equally leave a name that // is no longer repeated. Both are settled by renumbering the whole language section: a "Rengdêr" // beside a new "Rengdêr" becomes "Rengdêr 1" and "Rengdêr 2", and a number left behind by a name // that is now unique is dropped. var body = lines.join("\n"); var nextLang = body.indexOf("\n== ", 1); var end = nextLang === -1 ? body.length : nextLang; body = wt.numberRepeatedPosHeadings(body.slice(0, end)) + body.slice(end); return pageContent.slice(0, loc.langSectionIndex) + body; } // ---- "guhartina pêşketî" (advanced editing): combine several independent per-sense edits into ONE save ---- // Threads pageContent through the same pure functions every single-field form uses, applying only the // pieces the caller actually filled in. A piece that conflicts with the page (maneid already set, a // subsection that's already there) is skipped with a warning rather than failing the whole save. Throws // SubmitAbort if nothing was filled in, so this can never turn into a no-op save. function applyAdvancedChanges(pageContent, language, sectionType, sectionOrdinal, definitionLevel, changes) { var content = pageContent; var warnings = []; var appliedAnything = false; if (changes.gloss) { content = updateSenseGloss(content, language, sectionType, sectionOrdinal, definitionLevel, changes.gloss); appliedAnything = true; } if (changes.newSense) { content = insertNewSense(content, language, sectionType, sectionOrdinal, definitionLevel, changes.newSense, changes.newSenseExtras); appliedAnything = true; } // Every box below is prefilled with what the sense already has, so a save replaces that value rather // than merging into it -- and clearing a box removes the template. changes.changed marks the boxes // whose contents actually differ from the page, so an opened-but-untouched box writes nothing. var replace = { replace: true }; if (changes.changed.maneid) { content = insertLeadingSenseTpl(content, language, sectionType, sectionOrdinal, definitionLevel, "maneid", changes.maneid ? [changes.maneid] : [], replace); appliedAnything = true; } if (changes.changed.etiket) { content = insertLeadingSenseTpl(content, language, sectionType, sectionOrdinal, definitionLevel, "e", changes.etiket, replace); appliedAnything = true; } if (changes.changed.kategori) { content = insertLeadingSenseTpl(content, language, sectionType, sectionOrdinal, definitionLevel, "K", changes.kategori, replace); appliedAnything = true; } SENSE_GROUPS.forEach(function (group) { if (changes.changed["word:" + group.key]) { content = insertSenseWords(content, language, sectionType, sectionOrdinal, definitionLevel, group.key, changes.words[group.key], replace); appliedAnything = true; } }); if (!appliedAnything) { throw new SubmitAbort("Tiştek nehat guhartin."); } return { content: content, warnings: warnings }; } // ---- per-definition UI: a small always-visible "+" toggle (no hover, so it works on mobile/touch too) ---- // A growing list of word rows. The row itself is util.js's, shared with the wizard; this owns the list: // typing in the last row appends a fresh empty one, so there is always somewhere to type. var WORDLIST_COLUMN_THRESHOLD = 6; function buildWordListField(placeholder, opts) { opts = opts || {}; var $list = $("<ol class='dsb-wordlist'></ol>"); // One column is a long scroll once a list gets past a handful of words. The class only asks for // two; the stylesheet still gives one on a narrow screen. Two columns inside the panel's usual // 520px would leave each word about 250px, so the panel drops its width cap for as long as any // list in it is in two columns -- checked across the whole panel, since a "Herwiha binêre" holds // several groups and one of them going wide settles it for all. function updateColumns() { $list.toggleClass("dsb-wordlist-2col", $list.children("li").length > WORDLIST_COLUMN_THRESHOLD); var $panel = $list.closest(".dsb-subsection-panel"); $panel.toggleClass("dsb-panel-wide", !!$panel.find(".dsb-wordlist-2col").length); } function makeRow() { var row = util.makeModifierRow({ prefix: "dsb-", placeholder: placeholder, modTitle: util.msg.addModifier, removeTitle: util.msg.removeWord, removeModTitle: util.msg.removeModifier, modifiers: opts.modifiers, // "bixe qutiyan" puts each comma-separated word in a row of its own, right after the row it // was typed in, and the caret follows to the last of them. One word per row is also one // word per template parameter, which both {{hevmane}} and {{kol3}} read the same way -- // a comma left inside a parameter would not be. onSplit: function (rest) { var $after = row.$li; var last = null; rest.forEach(function (raw) { var extra = makeRow(); $after.after(extra.$li); $after = extra.$li; if (raw) extra.setRaw(raw); last = extra; }); // Always an empty row at the end to type the next word into. var $tail = $list.children("li").last(); if ($.trim($tail.data("row").getRaw())) { last = makeRow(); $list.append(last.$li); } updateColumns(); if (last) last.focus(); }, // raw === null means the row removed itself, so the count has changed either way. onChange: function (raw) { if (raw !== null) { var isLast = row.$li.is($list.children("li").last()); if (isLast && raw) $list.append(makeRow().$li); } updateColumns(); } }); row.$li.data("row", row); return row; } $list.append(makeRow().$li); return { $element: $list, values: function () { var vals = []; $list.children("li").each(function () { var raw = $(this).data("row").getRaw(); if (raw) vals.push(raw); }); return vals; }, // Replaces the whole list, keeping one empty trailing row to type into. setValues: function (raws) { $list.empty(); raws.forEach(function (raw) { var row = makeRow(); $list.append(row.$li); row.setRaw(raw); }); $list.append(makeRow().$li); updateColumns(); }, focus: function () { $list.children("li").first().data("row").focus(); } }; } // Plain removable-tag field, used for Etîket and Kategorî (simple value lists with no per-word modifiers). // Enter or "," commits the current text as a tag; Backspace on an empty input removes the last tag; // values() also commits whatever is still typed but not yet confirmed. function buildTagField(placeholder) { var $input = $("<input type='text' class='dsb-input dsb-tag-input'>").attr("placeholder", placeholder); var $element = $("<span class='dsb-tagfield'></span>").append($input); function currentValues() { var vals = []; $element.children(".dsb-tag").each(function () { vals.push($(this).data("value")); }); return vals; } function addTag(val) { val = $.trim(val || ""); if (!val || currentValues().indexOf(val) !== -1) { return; } var $tag = $("<span class='dsb-tag'></span>").text(val).data("value", val); var $remove = $("<button type='button' class='dsb-tag-remove' title='Jê bibe'>&#215;</button>"); $remove.on("click", function () { $tag.remove(); $input.trigger("focus"); }); $tag.append($remove); $tag.insertBefore($input); } $input.on("keydown", function (e) { if (e.which === 13 || e.which === 188) { // Enter or comma e.preventDefault(); addTag($input.val()); $input.val(""); } else if (e.which === 8 && !$input.val()) { $element.children(".dsb-tag").last().remove(); } }); $input.on("blur", function () { addTag($input.val()); $input.val(""); }); return { $element: $element, // Replaces every tag, for prefilling from what the sense already has. setValues: function (vals) { $element.children(".dsb-tag").remove(); vals.forEach(function (v) { addTag(v); }); $input.val(""); }, values: function () { addTag($input.val()); $input.val(""); return currentValues(); }, focus: function () { $input.trigger("focus"); } }; } // The wizard's own etymology templates, straight out of data-main.json's "etymTpls" -- the same list start.js's // insert menu offers, so the two can never drift apart. CODE/SOURCELANG are its placeholders. // The wizard's own template lists, straight out of data-main.json, so both gadgets offer the same // templates in the same order. A pronunciation template names the languages it is for; one that names // none is offered everywhere. function templateChoices(pool, language) { var defs = startData[pool] || {}; return Object.keys(defs).filter(function (name) { var langs = defs[name].langs; return !langs || !langs.length || $.inArray(language, langs) !== -1; }).map(function (name) { return { name: name, desc: defs[name].desc, snippet: defs[name].snippet }; }); } // One template call inside the surface: shown as its own wikitext, but atomic -- contenteditable="false" // keeps the caret out of it, so it can only be changed through the dialog. function makeTemplateChip(wikitext) { // The × is its own element rather than the chip's only way out: on a phone there is no reliable // backspace onto a contenteditable="false" span, so without it the dialog is the only way to take // a call off the line. readChipSurface() reads chips from data-wikitext, so the extra child does // not reach the saved text. var $remove = $("<span class='dsb-tpl-chip-remove' contenteditable='false'>&#215;</span>") .attr("title", "Vê şablonê jê bibe"); return $("<span class='dsb-tpl-chip' contenteditable='false' role='button' tabindex='0'></span>") .attr("data-wikitext", wikitext) .attr("title", "Bitikîne da ku parametreyan biguhêrî") .text(wikitext) .append($remove); } // wikitext -> surface DOM: template calls become chips, everything else stays plain editable text. function renderChipSurface($surface, wikitext) { var text = wikitext || ""; var calls = parseTemplateCalls(text).calls; var pos = 0; $surface.empty(); calls.forEach(function (call) { if (call.start > pos) { $surface.append(document.createTextNode(text.slice(pos, call.start))); } $surface.append(makeTemplateChip(text.slice(call.start, call.end))); pos = call.end; }); if (pos < text.length) { $surface.append(document.createTextNode(text.slice(pos))); } } // surface DOM -> wikitext. Chips give back their stored call; anything the browser inserted while typing // (a <br>, a wrapper <div>) degrades to its text, so the result is always plain wikitext. function readChipSurface(node) { var out = ""; $(node).contents().each(function () { if (this.nodeType === 3) { out += this.nodeValue; return; } var $el = $(this); if ($el.hasClass("dsb-tpl-chip")) { out += $el.attr("data-wikitext"); return; } if (this.nodeName === "BR") { out += "\n"; return; } if (this.nodeName === "DIV" || this.nodeName === "P") { out += "\n" + readChipSurface(this); return; } out += readChipSurface(this); }); return out; } // Drops a node in at the caret, or at the end when the caret is not inside the surface. function insertAtCaret($surface, node) { // Inserting a fragment moves its children into the surface and leaves the fragment itself empty // and parentless, so the caret follows that last child; setStartAfter on the fragment throws. var last = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? node.lastChild : node; var sel = window.getSelection(); if (sel && sel.rangeCount && $.contains($surface.get(0), sel.getRangeAt(0).commonAncestorContainer)) { var range = sel.getRangeAt(0); range.deleteContents(); range.insertNode(node); if (last) { range.setStartAfter(last); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } } else { $surface.append(node); } } // The template-param dialog (openTemplateDialog/buildTemplateParamForm) moved to wikt.cite, shared with // start.js's own Etîmolojî and Çavkanî pickers: cite.openTemplateDialog / cite.buildTemplateParamForm. // Each new example queued is its own entry, so several can be waiting at once. var exampleSeq = 0; // Takes the save over from add-examples while its form is open for one sense: its "Qeyd bike" becomes // "Lê zêde bike" and the edit joins the pending queue instead of publishing on its own, so examples and // whatever else is queued go up as one edit. Installed on every open, so the handler always closes over // the sense whose form is actually open. ctx: {$li, $h3, language, sectionType, sectionOrdinal, // definitionLevel, $save, $status, afterQueue}. function takeOverExampleSave(ctx) { if (!(wikt.addExamples && wikt.addExamples.setSubmitHandler)) { return; // older add-examples: it publishes on its own, as before } // Lets the form read an example as the queue has already changed it. wikt.addExamples.pendingContent = queuedContent; wikt.addExamples.setSubmitHandler(function (job) { exampleSeq++; queueChange( placeLabel(ctx.language, ctx.sectionType, ctx.$h3), // add-examples' own summary, so the combined edit still credits the tool and names its // version. The trailing stop goes: this is one item in a list, not a sentence of its own. (job.summary || "nimûne/jêgirtin hate zêdekirin").replace(/\.\s*$/, ""), function (pageContent) { // add-examples builds against a revision object, and returns {text, summary}. var built = job.transform({ content: pageContent }); return { content: built.text, warnings: [] }; }, ctx.$save, ctx.$status, // Drawn like any other queued change, in the sense it belongs to -- except a removal, // which strikes the line through where it sits, as "Maneyê rake" does for a sense. function () { if (job.removes && ctx.itemEl) { var $item = $(ctx.itemEl).addClass("dsb-example-doomed"); return function () { $item.removeClass("dsb-example-doomed"); }; } return previewSense(ctx.$li, ctx.language, ctx.sectionType, ctx.sectionOrdinal, ctx.definitionLevel); }, // An edit to an example already on the page brings its own key, so editing it again replaces it. { key: job.key || ("example:" + ctx.sectionType + "|" + ctx.sectionOrdinal + "|" + ctx.definitionLevel.join(".") + "|" + exampleSeq), sensePath: ctx.definitionLevel } ); job.done(); if (ctx.afterQueue) { ctx.afterQueue(); } }); } // Opens add-examples' form on one example already under a sense -- dsb.js's pencil on it. Its save joins // the queue like any other change. function editExample($li, $h3, language, sectionType, sectionOrdinal, definitionLevel, itemEl) { takeOverExampleSave({ $li: $li, $h3: $h3, language: language, sectionType: sectionType, sectionOrdinal: sectionOrdinal, definitionLevel: definitionLevel, $save: $(), $status: $(), itemEl: itemEl, afterQueue: openQueueBox }); if (!(wikt.addExamples && wikt.addExamples.editFor && wikt.addExamples.editFor($li.get(0), itemEl))) { mw.notify("Amûra nimûneyan li vê maneyê nehat dîtin."); } } // Builds the whole "guhartina pêşketî" panel into $quickRow: gloss and the two most-used word lists // (Hevmane/Dijmane) sit directly in the picker row; the remaining word lists, maneid, etîket, kategorî // and Nimûne/jêgirtin live behind a "Hevmane û Bêhtir" dropdown; one shared "Lê zêde bike" queues everything the // user filled in as a single edit via applyAdvancedChanges() and queueChange(). Nimûne/jêgirtin is an // immediate proxy-click into a different gadget's own form, not part of this combined save. // pending, when given, means this panel composes a sense that is not on the page yet: it edits no // line, and its save queues one insertion carrying the definition, its word lists and its // quotations. Everything the user sees is the panel's own, so a new sense is edited exactly like a // saved one. See openNewSenseRow(). function buildAdvancedPanel($quickRow, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense, pending) { // "#:" lines for a pending sense's quotations, in the order add-examples handed them over. var pendingQuotes = pending ? pending.state.quotes.slice() : []; var $pendingQuotes = $("<div class='dsb-new-sense-quotes'></div>"); // "+" marks the actions that add something, matching the dropdown's own "+ Hevmane" labels. var $nimune = $("<a href='#' class='dsb-quick-link'></a>").text("+ Nimûne/jêgirtin"); $nimune.on("click", function (e) { e.preventDefault(); if (pending) { collectQuotation(); $moreMenu.slideUp(); return; } takeOverExampleSave({ $li: $li, $h3: $h3, language: language, sectionType: sectionType, sectionOrdinal: sectionOrdinal, definitionLevel: definitionLevel, $save: $save, $status: $status }); // Toggle, so clicking this a second time closes the form again. add-examples builds nothing // on the page of its own, so this is the only way its form opens. var drive = wikt.addExamples.toggleFor || wikt.addExamples.openFor; if (!(drive && drive($li.get(0)))) { mw.notify("Amûra nimûneyan li vê maneyê nehat dîtin."); } $moreMenu.slideUp(); }); // The form has to be opened on a sense that exists, so it opens on the one this row hangs off; // only the line it builds is kept, and the form itself is moved under the row, title and all. function collectQuotation() { if (!(wikt.addExamples && wikt.addExamples.setSubmitHandler)) { mw.notify("Amûra nimûneyan ne berdest e."); return; } wikt.addExamples.setSubmitHandler(function (job) { if (!job.line) { job.fail("Ev guhartoya amûra nimûneyan nikare rêzê bide."); return; } pendingQuotes.push(job.line.replace(/^#+/, "")); $pendingQuotes.append($("<div class='dsb-new-sense-quote'></div>").text(job.line)); job.done(); }); if (!(wikt.addExamples.openFor && wikt.addExamples.openFor($li.get(0)))) { mw.notify("Amûra nimûneyan li vê maneyê nehat dîtin."); return; } // Its title is a list item in the sense's own example list; taken out of that list so it // does not read as a numbered sense of its own. var $title = $li.find(".add-example-form-title").first().closest("li"); var $form = $li.find(".add-example-form").first(); if ($title.length) { $pendingQuotes.after($("<div class='dsb-new-sense-form-title'></div>") .append($title.contents())); $title.remove(); } if ($form.length) { $quickRow.find(".dsb-new-sense-form-title").first().after($form); } } // getters holds one function per currently-open box (key -> () => value); collectChanges() only ever // reads from a box the user actually clicked to open. var getters = {}; // What the sense currently holds, fetched once and shared by every box that prefills from it. // collectChanges() also diffs against it, so an untouched box never queues a change. var original = null; var sensePromise = null; function loadSense() { if (!sensePromise) { if (pending) { original = { gloss: pending.state.text || "", maneid: pending.state.maneid || "", etiket: pending.state.etiket || [], kategori: pending.state.kategori || [], words: pending.state.lists || {} }; sensePromise = $.Deferred().resolve(original).promise(); } else { sensePromise = fetchSenseFields(language, sectionType, sectionOrdinal, definitionLevel).then(function (fields) { original = fields; return fields; }); } } return sensePromise.then(withQueued); } // The change already queued for this sense, as the fields it writes. Read back from the queue // rather than kept in a variable here, so dropping the entry from the queue box also drops this. function queuedSenseChanges() { if (pending) { return null; } var key = senseQueueKey(); var found = null; dsbQueue.entries.forEach(function (entry) { if (entry.key === key && entry.senseChanges) { found = entry.senseChanges; } }); return found; } // The sense with whatever is queued for it written over the top (nothing queued: as the page has it). function withQueued(fields) { return overlaySenseFields(fields, queuedSenseChanges()); } function sameList(a, b) { return (a || []).join("\u0000") === (b || []).join("\u0000"); } // -- gloss ("Maneyê biguhêre" -- the one box that EDITS the sense's wording; everything else here only ADDS) -- function addGlossField() { var $fields = $("<div class='dsb-gloss-fields'></div>"); var $textarea = $("<textarea class='dsb-input dsb-edit-gloss' rows='2'></textarea>") .attr("placeholder", util.msg.loading).prop("disabled", true); var $autoLink = $("<button type='button' class='dsb-autolink'>Oto-lînk bike</button>").prop("disabled", true); var $toRaw = $("<a href='#' class='dsb-quick-link dsb-gloss-raw'>Wekî wîkîtekst biguhêre</a>").hide(); var $status = $("<span class='dsb-status'></span>"); var originalGloss = null; var tplForm = null; // set while the parameter boxes are showing instead of the raw line var tplCall = null; // The gloss of a non-Kurdish entry is a translation followed by {{îzah|…}} explaining it. That // is one field on the page, so it stays one box here; the explanation opens beside it only when // asked for, and the two are joined back into a single gloss on save. A Kurdish sense is its // own definition, so it is never offered one -- though a ku gloss that somehow already carries // an {{îzah}} still opens its box below, rather than being edited blind. var offerIzah = language !== "ku"; var $izah = $("<textarea class='dsb-input dsb-edit-izah' rows='2'></textarea>") .attr("placeholder", "Îzaha maneyî… ji bo school-a îngilîzî 'cihê tehsîlgirtinê'"); // A non-Kurdish gloss is a translation and its explanation, so both boxes open together. A Kurdish // sense is its own definition and gets no îzah box, unless its gloss already carries one. var $izahWrap = $("<div class='dsb-izah-wrap'></div>") .append($("<label class='dsb-adv-label'>Îzaha maneyî</label>").append($izah)) .toggle(offerIzah); $fields.append($textarea); // The template form edits one call inside the line, so its result is spliced back into the rest. function currentGloss() { if (tplForm) { return $.trim(originalGloss.slice(0, tplCall.start) + tplForm.values() + originalGloss.slice(tplCall.end)); } return joinGloss($.trim($textarea.val()), $.trim($izah.val())); } function showRaw() { tplForm = null; tplCall = null; $fields.empty().append($textarea); $autoLink.show(); $toRaw.hide(); } loadSense().then( function (fields) { originalGloss = fields.gloss; // An existing gloss that already ends in {{îzah|…}} comes apart into the two boxes, so // editing it does not mean editing template syntax by hand. var parts = util.splitTercimeIzah(fields.gloss); $textarea.val(parts ? $.trim(parts.tercime) : fields.gloss) .prop("disabled", false) // A non-Kurdish gloss is a translation; the box under it takes the explanation. .attr("placeholder", offerIzah ? util.msg.tercimePrompt : "Maneyê binivîse…"); if (parts) { $izah.val($.trim(parts.izah)); $izahWrap.show(); } $autoLink.prop("disabled", false); var target = glossTemplateTarget(fields.gloss); if (!target) { return; } fetchTemplateData(target.name).then(function (td) { // The fetch is async; leave the raw box alone if it has already been typed into. if ($.trim($textarea.val()) !== $.trim(originalGloss)) { return; } tplCall = target; tplForm = cite.buildTemplateParamForm(target, td); $fields.empty().append(tplForm.$element); $autoLink.hide(); // linkifying a bare template call makes no sense // A gloss that is one template call carries no separate explanation. $izahWrap.hide(); $toRaw.show(); }); }, function (codeOrAbort, result) { $textarea.attr("placeholder", ""); var message = codeOrAbort instanceof SubmitAbort ? codeOrAbort.message : "Nikare maneya heyî bar bike: " + apiErrorMessage(codeOrAbort, result); $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text(message); } ); $autoLink.on("click", function () { var mane = $.trim($textarea.val()); var izah = $.trim($izah.val()); if (!mane && !izah) { return; } $autoLink.prop("disabled", true); $status.removeClass("dsb-status-ok dsb-status-error").text("Tê lînkkirin…"); linkifyGlossParts(mane, izah, function (linkedMane, linkedIzah, error) { $textarea.val(linkedMane); $izah.val(linkedIzah); $autoLink.prop("disabled", false); if (error) { $status.addClass("dsb-status-error").text(error); } else { $status.text(""); } }); }); $toRaw.on("click", function (e) { e.preventDefault(); $textarea.val(currentGloss()); // read the form before showRaw() discards it showRaw(); }); getters.gloss = function () { var v = currentGloss(); // A pending sense has no line on the page to differ from: whatever is in the box is the sense. return (pending || (v && v !== originalGloss)) ? (v || null) : null; }; return $("<div class='dsb-adv-group'></div>").append( $("<div class='dsb-adv-label'></div>").text("Mane"), $fields, $izahWrap, $("<div class='dsb-form-actions'></div>").append($autoLink, $toRaw, $status) ); } // -- one SENSE_GROUPS word-list box (Hevmane/Dijmane/etc.) -- function addWordField(group) { var field = buildWordListField(group.label + "…"); loadSense().then(function (fields) { var current = fields.words[group.key]; if (current && current.length) { field.setValues(current); } }); getters["word:" + group.key] = function () { return field.values(); }; // The hint is senseGroups' own, so the box carries the same example sentence the wizard puts // beside its copy of this list. var $label = $("<div class='dsb-adv-label'></div>").text(group.label); if (group.hint) { $label.append($("<span class='dsb-adv-hint'></span>").text(group.hint)); } return $("<div class='dsb-adv-group'></div>").append($label, field.$element); } // -- maneid (single value; re-adding one that already exists is rejected -- see applyAdvancedChanges) -- // "Maneyeke din" opens a row of its own directly under this sense, so the list numbers it as the // sense it is about to become, and it carries its own buttons: everything it collects -- the // definition, its word lists, its quotations -- goes onto the page as a single insertion, because a // sense that is not saved yet has no line number for a second change to find (see insertNewSense). var newSenseKey = "newSense:" + definitionLevel.join("."); function queuedNewSense() { var found = null; dsbQueue.entries.forEach(function (entry) { if (entry.key === newSenseKey) { found = entry; } }); return found; } function dropQueuedNewSense() { dsbQueue.entries.slice().forEach(function (entry) { if (entry.key === newSenseKey) { dropQueueEntry(entry); } }); } // A sense that is not on the page yet is edited with this same panel, in a row of its own: same // picker, same dropdown, same buttons. Only the save differs -- it writes an insertion rather // than editing a line -- and the row renders what the insertion will say. function openNewSenseRow(state) { var existing = $quickRow.data("dsbNewSenseRow"); if (existing && existing.closest("body").length && !state) { existing.find("textarea").first().trigger("focus"); return; } if (existing) { existing.remove(); } if (state) { // The rendered row belongs to the queue entry: hide it while the box is open, and leave // the entry alone so cancelling can put it back. $li.nextAll("li.dsb-new-sense-pending").first().remove(); } openQueueBox(); var $row = $("<li class='dsb-sense-preview dsb-new-sense-row'></li>"); var $panel = $("<div class='dsb-quick-row dsb-new-sense'></div>"); $row.append($panel); $li.after($row); $quickRow.data("dsbNewSenseRow", $row); buildAdvancedPanel($panel, $li, $h3, language, sectionType, sectionOrdinal, definitionLevel, isLastSense, { state: state || { text: "", lists: {}, quotes: [], maneid: "", etiket: [], kategori: [] }, key: newSenseKey, $row: $row, close: function () { $row.remove(); $quickRow.removeData("dsbNewSenseRow"); hideQueueIfIdle(); }, reopen: openNewSenseRow, queued: queuedNewSense, drop: dropQueuedNewSense }); $panel.show(); $panel.find("textarea").first().trigger("focus"); } function addManeidField() { var $input = $("<input type='text' class='dsb-input' placeholder='id…'>"); loadSense().then(function (fields) { $input.val(fields.maneid); }); getters.maneid = function () { return $.trim($input.val()); }; return $("<div class='dsb-adv-group'></div>").append($("<div class='dsb-adv-label'></div>").text("Maneid"), $input); } // -- etîket / kategorî -- function addEtiketField() { var field = buildTagField("Etîket…"); loadSense().then(function (fields) { field.setValues(fields.etiket); }); getters.etiket = function () { return field.values(); }; return $("<div class='dsb-adv-group'></div>").append($("<div class='dsb-adv-label'></div>").text("Etîket"), field.$element); } function addKategoriField() { var field = buildTagField("Kategorî…"); loadSense().then(function (fields) { field.setValues(fields.kategori); }); getters.kategori = function () { return field.values(); }; return $("<div class='dsb-adv-group'></div>").append($("<div class='dsb-adv-label'></div>").text("Kategorî"), field.$element); } // -- the picker row: the gloss and Nimûne/jêgirtin links sit directly in the row; every word group and // the rest live behind a "Hevmane û Bêhtir" dropdown. Clicking any link builds and appends its box // to $openBoxes and hides that link; the box's own "×" removes it, clears its getter, and un-hides the // link -- several boxes can be open at once. No "Beşek (====)" entry here: buildSubsectionPanel()'s // own "+ Beşek lê zêde bike" trigger already covers page-level subsections. ---- var FIELD_DEFS = [{ key: "gloss", label: GLOSS_LABEL, build: addGlossField, primary: true }] .concat(SENSE_GROUPS.map(function (group) { return { key: "word:" + group.key, label: group.label, build: function () { return addWordField(group); }, primary: false }; })) .concat([ { key: "maneid", label: "Maneid", build: addManeidField, primary: false }, { key: "etiket", label: "Etîket", build: addEtiketField, primary: false }, { key: "kategori", label: "Kategorî", build: addKategoriField, primary: false } ]); // Every sense offers this: the new one lands directly below whichever sense asked for it (see // insertNewSense), so it is no longer only the last one that can add to the end. // No build(): its link opens a row of its own (openNewSenseRow), not a box in this panel. FIELD_DEFS.push({ key: "newSense", label: util.msg.addAnotherSense, primary: false }); var $picker = $("<div class='dsb-field-picker'></div>"); var $moreMenu = $("<div class='dsb-more-menu'></div>").hide(); var $openBoxes = $("<div class='dsb-open-boxes'></div>"); // $actions (built below) stays hidden until at least one field box is actually open -- otherwise // "Lê zêde bike" sits there enabled with nothing to queue, which just produces an empty no-op change. var openBoxCount = 0; function updateActionsVisibility() { $actions.toggle(openBoxCount > 0); } function makeFieldLink(def) { var $link = $("<a href='#' class='dsb-quick-link dsb-field-pick'></a>").text(def.key === "gloss" ? def.label : "+ " + def.label); $link.on("click", function (e) { e.preventDefault(); if (def.key === "newSense") { $moreMenu.slideUp(); openNewSenseRow(); return; } openQueueBox(); var $box = def.build(); var $remove = $("<a href='#' class='dsb-box-remove' title='Rake'>×</a>"); $remove.on("click", function (e2) { e2.preventDefault(); delete getters[def.key]; $box.remove(); $link.show(); openBoxCount--; updateActionsVisibility(); }); $box.prepend($remove); $openBoxes.append($box); util.initManualResize($box); $link.hide(); $moreMenu.slideUp(); openBoxCount++; updateActionsVisibility(); }); return $link; } // Destructive, so it lives at the bottom of the dropdown behind a confirmation, and shares the // sense's queue key: deleting supersedes any edit still pending on the same sense. function makeDeleteLink() { var $link = $("<a href='#' class='dsb-quick-link dsb-field-delete'>Maneyê rake</a>"); $link.on("click", function (e) { e.preventDefault(); $moreMenu.slideUp(); if (pending) { // Nothing to remove from the page: the sense only exists in the queue. pending.drop(); pending.close(); return; } OO.ui.confirm("Ev mane û hemû rêzên bin wê dê werin rakirin. Dewam bike?").done(function (ok) { if (!ok) { return; } queueChange( placeLabel(language, sectionType, $h3), "maneyek hate rakirin", function (baseContent) { return removeSense(baseContent, language, sectionType, sectionOrdinal, definitionLevel); }, $save, $note, function () { $li.addClass("dsb-sense-doomed"); return function () { $li.removeClass("dsb-sense-doomed"); }; }, { key: senseQueueKey(), sensePath: definitionLevel, senseLanguage: language, removesSense: true } ); $note.show(); }); }); return $link; } // Only the links the row actually shows are built now; the dropdown's nine are 43% of the work and // nobody sees them until "Hevmane û Bêhtir" is clicked, so buildMenuLinks() waits for that. FIELD_DEFS.forEach(function (def) { if (!def.primary || (pending && def.key === "newSense")) { return; } $picker.append(makeFieldLink(def)); if (def.key === "gloss") { $picker.append($nimune); // directly after "Maneyê biguhêre" } }); // Three sections, ruled apart: what labels this sense, the word lists that hang off it, and adding // another sense. "Maneyê rake" is appended after these by makeDeleteLink() and so closes the last // one. The word lists follow senseGroups' "order" -- that array's own order is the order the "#:" // lines are written to the page in, which is not a reading order; the wizard's boxes read the same // field, so both gadgets list them alike. Anything not named here still appears, after these, so a // field added later is never silently dropped from the menu. var MENU_GROUPS = [ ["etiket", "kategori", "maneid"], SENSE_GROUPS.slice().sort(function (a, b) { return (a.order || 0) - (b.order || 0); }).map(function (g) { return "word:" + g.key; }), ["newSense"] ]; var menuBuilt = false; function buildMenuLinks() { if (menuBuilt) { return; } menuBuilt = true; var byKey = {}; FIELD_DEFS.forEach(function (def) { if (!def.primary && !(pending && def.key === "newSense")) { byKey[def.key] = def; } }); var placed = {}; MENU_GROUPS.forEach(function (group, groupIndex) { var defs = group.map(function (key) { return byKey[key]; }).filter(Boolean); defs.forEach(function (def, i) { placed[def.key] = true; var $link = makeFieldLink(def); // The rule goes on the first entry of every group after the first. if (groupIndex > 0 && i === 0) { $link.addClass("dsb-menu-group-start"); } $moreMenu.append($link); }); }); FIELD_DEFS.forEach(function (def) { if (!def.primary && !placed[def.key] && !(pending && def.key === "newSense")) { $moreMenu.append(makeFieldLink(def)); } }); $moreMenu.append(makeDeleteLink()); } // Lives inside $picker (not appended straight to $quickRow) so .dsb-more-menu's CSS can position it as // an overlay anchored to $picker, instead of pushing $openBoxes/the rest of the page down when it opens. var $moreArrow = $("<span class='dsb-more-arrow'></span>").text("▸"); var $moreToggle = $("<a href='#' class='dsb-quick-link dsb-more-toggle'></a>") .text(util.msg.moreMenu + " ").append($moreArrow); $moreToggle.on("click", function (e) { e.preventDefault(); buildMenuLinks(); $moreMenu.slideToggle(function () { $moreArrow.text($moreMenu.is(":visible") ? "▾" : "▸"); }); }); $picker.append($moreToggle, $moreMenu); // Outside clicks are handled by one shared document listener (see closeMoreMenus), not one per panel: // hovering a sense builds its panel, so a long entry would otherwise accumulate dozens of them. $moreMenu.data("dsbArrow", $moreArrow); // -- combined save: whatever boxes are open right now, queued together via queueChange(); the actual // save happens when the user clicks "Guhartinan qeyd bike" on the floating queue box. ---- // Small "not saved yet" preview of everything this queued change will do, appended right under the // sense's gloss (plain text, not a real render of {{hevmane}}/etc.). function collectChanges() { // Against what the boxes were filled from, queued change and all -- a box reopened and left // alone writes nothing, and one the user has just emptied counts as emptied. var base = withQueued(original || { maneid: "", etiket: [], kategori: [], words: {} }); var changed = {}; var words = {}; SENSE_GROUPS.forEach(function (group) { var getter = getters["word:" + group.key]; words[group.key] = getter ? getter() : []; if (getter && !sameList(words[group.key], base.words[group.key])) { changed["word:" + group.key] = true; } }); var maneid = getters.maneid ? getters.maneid() : ""; var etiket = getters.etiket ? getters.etiket() : []; var kategori = getters.kategori ? getters.kategori() : []; if (getters.maneid && maneid !== (base.maneid || "")) { changed.maneid = true; } if (getters.etiket && !sameList(etiket, base.etiket)) { changed.etiket = true; } if (getters.kategori && !sameList(kategori, base.kategori)) { changed.kategori = true; } return { gloss: getters.gloss ? getters.gloss() : null, maneid: maneid, etiket: etiket, kategori: kategori, words: words, changed: changed }; } // One insertion carrying the whole sense: its leading calls and definition on the "#" line, then // a "#:" line per word list and per quotation, in the order the wizard writes them. function newSenseWikitext(changes) { var leading = LEADING_TPL_ORDER.map(function (key) { var values = key === "maneid" ? (changes.maneid ? [changes.maneid] : []) : key === "e" ? changes.etiket : key === "K" ? changes.kategori : []; return values && values.length ? "{{" + key + "|" + language + "|" + values.join("|") + "}}" : null; }).filter(Boolean).join(" "); var lines = []; SENSE_GROUPS.forEach(function (group) { var words = changes.words[group.key] || []; if (words.length) { lines.push(": {{" + group.key + "|" + language + "|" + words.join("|") + "}}"); } }); lines = lines.concat(pendingQuotes); return { text: (leading ? leading + " " : "") + $.trim(changes.gloss || ""), extras: lines }; } function pendingState(changes) { return { text: $.trim(changes.gloss || ""), lists: changes.words, quotes: pendingQuotes.slice(), maneid: changes.maneid, etiket: changes.etiket, kategori: changes.kategori }; } function queueNewSense(changes) { if (!$.trim(changes.gloss || "")) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Maneya nû vala ye."); return; } var built = newSenseWikitext(changes); var held = pendingState(changes); var block = ["# " + built.text].concat(built.extras.map(function (extra) { return "#" + extra; })).join(NEWLINE); pending.close(); queueChange( placeLabel(language, sectionType, $h3), "maneyeke nû hate zêdekirin", function (baseContent) { return { content: insertNewSense(baseContent, language, sectionType, sectionOrdinal, definitionLevel, built.text, built.extras), warnings: [] }; }, $save, $status, // Drawn fresh on every call: redrawPreviews() runs this again whenever anything else // leaves the queue, and the row it drew last time is gone by then. function () { var $pending = $("<li class='dsb-sense-preview dsb-new-sense-pending'></li>"); $li.after($pending); var $edit = $("<span class='dsb-example-toolbar'></span>").append( $("<a href='#' class='dsb-toggle'></a>").attr("title", GLOSS_LABEL) .on("click", function (e) { e.preventDefault(); e.stopPropagation(); pending.reopen(held); })); renderWikitext(block).then(function (html) { if (!$pending.closest("body").length) { return; } var $content = $("<div class='mw-parser-output'></div>") .html(parsedSenseItem(html, 1).html); var $block = $content.children("dl, ul, ol").first(); if ($block.length) { $block.before($edit); } else { $content.append($edit); } $pending.empty().append($content); announceContent($pending); }, function () { $pending.empty().append($("<span></span>").text(built.text), $edit); }); return function () { $pending.remove(); }; }, { key: pending.key, sensePath: definitionLevel, senseLanguage: language } ); } // Both the field save and "Maneyê rake" queue under this, so the later one supersedes the earlier. function senseQueueKey() { return "sense:" + sectionType + "|" + sectionOrdinal + "|" + definitionLevel.join("."); } var $save = $("<button type='button' class='dsb-save'></button>").text(util.msg.add); // Where queueChange() writes its confirmation for actions that have no box of their own. var $note = $("<div class='dsb-status dsb-quick-note'></div>").hide(); var $cancel = $("<button type='button' class='dsb-cancel'></button>").text(util.msg.cancel); var $status = $("<span class='dsb-status'></span>"); var $actions = $("<div class='dsb-form-actions'></div>").append($save, $cancel, $status).hide(); // Emptying the boxes is what says the change has been taken: they were left open with their // text in them, so "Lê zêde bike" could be clicked again and queue the same values twice. function closeOpenBoxes() { $openBoxes.find(".dsb-box-remove").trigger("click"); } $cancel.on("click", function () { if (pending) { pending.close(); if (pending.queued()) { redrawPreviews(); } return; } // Close the boxes too, not just the row. Hiding alone left every field still open with its // text in it, so the next click on the pencil reopened the half-filled panel that had just // been cancelled. Each box's own "x" already clears its getter, restores its link and updates // the counter, so those are triggered rather than repeating the teardown here. $openBoxes.find(".dsb-box-remove").trigger("click"); $note.hide().text(""); // dsb.js owns the pencil's pinned state; tell it rather than hiding behind its back, or the // next click on the pencil would only toggle "pinned" off and appear to do nothing. $quickRow.trigger("dsbCancel"); hideQueueIfIdle(); }); $save.on("click", function () { var changes = collectChanges(); function afterLinkify() { if (pending) { queueNewSense(changes); return; } // This save supersedes any change still queued for the same sense, so it has to write that // one's fields too: the boxes it was made from are closed by now and collectChanges() cannot // see them. var merged = mergeSenseChanges(queuedSenseChanges(), changes); var parts = senseChangeLabels(merged); queueChange( placeLabel(language, sectionType, $h3), parts.length ? parts.join(", ") + (parts.length > 1 ? " hatin guhartin" : " hate guhartin") : "mane hate guhartin", function (baseContent) { return applyAdvancedChanges(baseContent, language, sectionType, sectionOrdinal, definitionLevel, merged); }, $save, $status, function () { return previewSense($li, language, sectionType, sectionOrdinal, definitionLevel); }, // Re-saving this same sense supersedes its pending change rather than queuing another. { key: senseQueueKey(), sensePath: definitionLevel, senseChanges: merged } ); closeOpenBoxes(); } $save.prop("disabled", true); // Auto-linkify the gloss as part of this save, but only a half nobody has linked by hand. // Split back into its two halves first, so the explanation is linked on its own rather than // sitting unseen inside {{îzah|…}}. if (changes.gloss) { $status.removeClass("dsb-status-ok dsb-status-error").text("Tê lînkkirin…"); var glossParts = util.splitTercimeIzah(changes.gloss); linkifyGlossParts( glossParts ? $.trim(glossParts.tercime) : changes.gloss, glossParts ? $.trim(glossParts.izah) : "", function (linkedMane, linkedIzah, error) { // The change is queued either way, so a gloss that came back empty is not written // back -- that would save the sense with its wording gone. var joined = joinGloss(linkedMane, linkedIzah); if ($.trim(joined)) { changes.gloss = joined; } if (error) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text(error); } afterLinkify(); }, true ); } else { afterLinkify(); } }); // The links sit under the buttons, so each open box pushes them down rather than leaving them far above. $quickRow.empty().append($openBoxes, $actions, $picker, $note); if (pending) { // The gloss box is what a new sense is: it opens with the row, and the quotations the row // collects sit above the actions. Done here, once $actions exists. $quickRow.append($pendingQuotes); pending.state.quotes.forEach(function (extra) { $pendingQuotes.append($("<div class='dsb-new-sense-quote'></div>").text("#" + extra)); }); $picker.children(".dsb-field-pick").filter(function () { return $(this).text() === GLOSS_LABEL; }).trigger("click"); } } // Attaches the pencil toggle to one leaf definition <li> -- clicking it reveals the "guhartina pêşketî" // (advanced editing) panel built by buildAdvancedPanel() above. // ---- per-section UI: "+ Beşek lê zêde bike" after a POS block's own content ---- // Reads the header text of "====" subsections already rendered under this POS heading, straight from the DOM. // The subsections of a part of speech are the headings one deeper than it is. function subsectionDepthFor($h3) { return (sections.headingDepth($h3) || 3) + 1; } function isSubsectionHeading($el, depth) { return sections.headingDepth($el) === depth; } function existingSubsectionHeadersInDom($h3) { var depth = subsectionDepthFor($h3); var headers = []; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && here < depth) { break; // out of this part of speech } if (isSubsectionHeading($cursor, depth)) { var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); headers.push(headingText($hh)); } } return headers; } // Finds where a new "====" subsection preview belongs in the live DOM, using the same SUBSECTION_ORDER rank // insertPageSubsection() uses. Returns {insertBefore: $el} (a ranked subsection that outranks this one) or // {insertAfter: $el} (goes at the end of the block). function domSubsectionInsertAnchor($h3, header) { var depth = subsectionDepthFor($h3); var myRank = subsectionRankForHeader(header); var $last = headingOuter($h3); for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && here < depth) { break; // out of this part of speech } if (isSubsectionHeading($cursor, depth)) { var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); var otherRank = subsectionRankForHeader(headingText($hh)); if (otherRank !== -1 && otherRank > myRank) { return { insertBefore: $cursor }; } } $last = $cursor; } return { insertAfter: $last }; } // The wikitext one subsection's content becomes, exactly as insertPageSubsection() writes it -- so the // preview parses the same string the save will produce. function subsectionBodyWikitext(language, s, content) { if (s.kind !== "wordlist") { return content; } return content.filter(function (group) { return group.words.length; }).map(function (group) { return buildWordListCall(WORDLIST_TEMPLATE, language, withNamedParam(["cure=" + s.header], "sernav", group.sernav), group.words); }).join("\n\n"); } // The plain word list this preview falls back to when the parse fails: the values that were typed, // without the template around them. Right, but not what the page will look like. function subsectionPlainDom($wrap, s, content) { if (s.kind !== "wordlist") { $wrap.append($("<p class='dsb-preview-text'></p>").text(content)); return; } content.forEach(function (group) { if (group.sernav) { $wrap.append($("<div class='dsb-preview-subheading'></div>").text(group.sernav)); } var $list = $("<ul class='dsb-preview-list'></ul>"); group.words.forEach(function (w) { $list.append($("<li></li>").text(w.replace(/<[^<>]*>/g, ""))); }); $wrap.append($list); }); } // A "not saved yet" preview of a page-level "====" subsection, dropped into the live page at the // position the save will put it, with its body rendered the way the page renders it -- real {{kol3}} // columns and real links, not a list of the words that went into them. Returns the inserted element so // the caller can remove it again. function buildSubsectionPreviewDom($h3, s, content, language) { var $wrap = $("<div class='dsb-preview-block'></div>"); // A styled <div>, not a real <h4>, so existingSubsectionHeadersInDom() never mistakes this live-only // preview for an actual subsection already on the page. $wrap.append($("<div class='dsb-preview-heading' role='heading' aria-level='4'></div>").text(s.header)); $wrap.append($("<span class='dsb-preview-badge'></span>").text("nû — hîn nehatiye qeydkirin")); // Placed before it is filled: a cached render resolves almost at once, and filling a wrap that is // still detached would announce content nobody can see. var anchor = domSubsectionInsertAnchor($h3, s.header); if (anchor.insertBefore) { anchor.insertBefore.before($wrap); } else { anchor.insertAfter.after($wrap); } var wikitext = language ? subsectionBodyWikitext(language, s, content) : ""; if ($.trim(wikitext)) { var $body = $("<div class='dsb-preview-rendered'></div>").appendTo($wrap); fillWithRendered($body, wikitext, function () { $body.remove(); subsectionPlainDom($wrap, s, content); }); } else { subsectionPlainDom($wrap, s, content); } return $wrap; } // A free section owns what lies between its own heading and the next heading of any depth, matching // the range locateHeadingByText() edits. Returns a function that puts the page back, or null. function replaceFreeSectionInPlace($heading, wikitext) { var $start = headingOuter($heading); var body = []; for (var $cursor = $start.next(); $cursor.length; $cursor = $cursor.next()) { if (sections.headingDepth($cursor)) { break; } if (!isOwnFurniture($cursor)) { body.push($cursor.get(0)); } } if (!body.length) { return null; } var $slot = $("<div class='dsb-sense-preview'></div>").insertAfter($start); var $body = $(body).detach(); fillWithRendered($slot, wikitext, function () { $slot.remove(); $slot = null; $body.insertAfter($start); }); return function () { if ($slot) { $body.insertAfter($start); $slot.remove(); $slot = null; } }; } /** * Redraws a subsection that is already on the page with the content this edit would give it. * * The section's own nodes -- everything between its heading and the next heading of the same rank or * higher -- are detached rather than dropped, so the returned function puts the page back exactly as it * was when the change is cancelled or superseded. */ function replaceSubsectionInPlace($h3, header, wikitext) { var depth = subsectionDepthFor($h3); // Walked the same way existingSubsectionHeadersInDom() walks it, so "which nodes are this // subsection" is decided once and the same way everywhere. var $start = null; var body = []; for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && here < depth) { break; // out of this part of speech } if (isSubsectionHeading($cursor, depth)) { if ($start) { break; // the next subsection starts here } var $hh = $cursor.is("h2, h3, h4, h5, h6") ? $cursor : $cursor.find("h2, h3, h4, h5, h6").first(); if (headingText($hh) === header) { $start = $cursor; } continue; } // This gadget's own furniture stays where it is: the subsection's editing panel is the sibling // right after its heading, so sweeping it into the body would detach the very panel the change // was made in and leave the pencil toggling an element that is no longer on the page. if ($start && !isOwnFurniture($cursor)) { body.push($cursor.get(0)); } } if (!$start || !body.length) { return null; } var $slot = $("<div class='dsb-sense-preview'></div>").insertAfter($start); var $body = $(body).detach(); fillWithRendered($slot, wikitext, function () { $slot.remove(); $slot = null; $body.insertAfter($start); }); return function () { if ($slot) { $body.insertAfter($start); $slot.remove(); $slot = null; } }; } /** * The live DOM one "place" section occupies: {nodes, insert}. nodes are what a render replaces -- * empty when the section is not on the page yet -- and insert() puts an element where the section * belongs, so a new one appears exactly where the save will put it. */ // This gadget's own panels sit between a heading and the page's content, so every walk steps over // them rather than counting one as content. function isOwnFurniture($el) { return $el.is(".dsb-toolbar, .dsb-quick-row, .dsb-subsection-panel, .dsb-convert-pos-panel," + " .dsb-preview-block, .dsb-sense-preview, .dsb-preview-inline"); } // A heading's place in the page, which orders queued renames and removals bottom-up. function headingPosition($el) { var $h = $el.is("h2, h3, h4, h5, h6") ? $el : $el.find("h2, h3, h4, h5, h6").first(); return $("h2, h3, h4, h5, h6").index($h); } // One subsection's own nodes on the page, read the way headingSpan() reads the wikitext: a "====" // subsection down to the next heading at its depth or shallower, a free section ($h3 is its heading) // down to the next heading of any depth. hasDeeper says a free section has headings under it. function subsectionDom($h3, s) { var $start = null; var depth; if (s.free) { $start = headingOuter($h3); depth = sections.headingDepth($start) || sections.headingDepth($h3); } else { depth = subsectionDepthFor($h3); for (var $c = headingOuter($h3).next(); $c.length; $c = $c.next()) { var d = sections.headingDepth($c); if (d && d < depth) { break; } if (d === depth) { var $hh = $c.is("h2, h3, h4, h5, h6") ? $c : $c.find("h2, h3, h4, h5, h6").first(); if (headingText($hh) === s.header) { $start = $c; break; } } } } if (!$start) { return null; } var body = []; var hasDeeper = false; for (var $cursor = $start.next(); $cursor.length; $cursor = $cursor.next()) { var here = sections.headingDepth($cursor); if (here && (s.free || here <= depth)) { hasDeeper = !!s.free && here > depth; break; } if (!isOwnFurniture($cursor)) { body.push($cursor.get(0)); } } return { $heading: $start, $body: $(body), hasDeeper: hasDeeper }; } function loosePlaceDom($h3, subsection) { var place = subsection.place; var nodes = []; var $cursor; if (place === "page-top") { // Şablon:herwiha binêre renders as <div class="rellink binere"> at the head of the content. var $out = $("#mw-content-text .mw-parser-output").first(); var $hat = $out.children(".binere, .rellink").first(); if ($hat.length) { return { nodes: $hat.get(), insert: function ($el) { $hat.before($el); } }; } return { nodes: [], insert: function ($el) { $out.prepend($el); } }; } var $start = headingOuter($h3); if (place === "pron") { // A "===" heading above this POS block, under the language one. for (var $back = $start.prev(); $back.length; $back = $back.prev()) { if ($back.is(".mw-heading2, h2")) { break; } if ($back.is(".mw-heading3, h3")) { var $hh = $back.is("h3") ? $back : $back.find("h3").first(); if (headingText($hh) === subsection.header) { for ($cursor = $back.next(); $cursor.length && !$cursor.is(".mw-heading2, .mw-heading3, h2, h3"); $cursor = $cursor.next()) { if (!isOwnFurniture($cursor)) { nodes.push($cursor.get(0)); } } return { nodes: nodes, insert: function ($el) { $back.after($el); } }; } } } return { nodes: [], insert: function ($el) { $start.before($el); } }; } if (place === "pos-top") { // The thumbnails between the heading and the headword line. for ($cursor = $start.next(); $cursor.length; $cursor = $cursor.next()) { if (isOwnFurniture($cursor)) { continue; } if (!$cursor.is(".thumb, figure, .floatright, .floatleft")) { break; } nodes.push($cursor.get(0)); } return { nodes: nodes, insert: function ($el) { $start.after($el); } }; } return null; } // The wikitext a "place" section's pending content renders as. A word list is its one call; everything // else is already the body that will be written. function loosePlacePreviewWikitext(subsection, content, language) { return subsection.kind === "wordlist" ? wt.buildHbnrCall(loosePlaceWords(content)) : loosePlaceBody(subsection, content, language); } // Draws a section's headword line from wikitext where the page's own line stands, or above the // definitions when the section has none. The page's line is detached rather than dropped, so the // returned function puts it back. function replaceHeadwordInPlace($h3, wikitext) { if (!$.trim(wikitext || "")) { return null; } var $old = $(); var $at = $(); for (var $cursor = headingOuter($h3).next(); $cursor.length; $cursor = $cursor.next()) { if ($cursor.is("p")) { $old = $cursor; break; } if (sections.headingDepth($cursor) || $cursor.is("ol")) { $at = $cursor; break; } } var $slot = $("<div class='dsb-sense-preview'></div>"); if ($old.length) { $old.before($slot); } else if ($at.length) { $at.before($slot); } else { return null; } var $body = $old.detach(); function restore() { if (!$slot) { return; } $body.insertBefore($slot); $slot.remove(); $slot = null; } fillWithRendered($slot, wikitext, restore); return restore; } // Redraws one of those places where it stands, the same way replaceSubsectionInPlace() redraws a // "====" one. The page's own nodes are detached rather than dropped, so cancelling puts them back. function replaceLoosePlaceInPlace($h3, subsection, wikitext) { if (!$.trim(wikitext || "")) { return null; } var at = loosePlaceDom($h3, subsection); if (!at) { return null; } var $slot = $("<div class='dsb-sense-preview'></div>"); at.insert($slot); var $body = $(at.nodes).detach(); function restore() { if (!$slot) { return; } $body.insertBefore($slot); $slot.remove(); $slot = null; } fillWithRendered($slot, wikitext, restore); return restore; } // Builds the subsection picker + its word-list/text field. A subsection that isn't on the page yet is saved // via insertPageSubsection(); one that already is gets prefilled and saved via updatePageSubsection(). // onlyHeader pins the panel to a single subsection (the per-heading pencil); omit it for the full picker. // free pins the panel to a heading that is not a subsection of a part of speech, located by its own // text: {header, ordinal}. function buildSubsectionPanel($panel, $h3, language, sectionType, sectionOrdinal, onlyHeader, free) { var existing = existingSubsectionHeadersInDom($h3); // A heading the data does not name is still editable. It is declared a word list so the reader // tries, in order, its {{kol3}} calls and then a plain bullet list; anything else falls through to // raw wikitext, which is what prefill() does with a list it cannot read. // Hidden counts as unknown here: the picker does not offer to build a Werger box, but one already // on the page still opens, as the wikitext it is. var unknown = onlyHeader && !PAGE_SUBSECTIONS.some(function (s) { return s.header === onlyHeader && !s.hidden; }); // A free section starts as wikitext: what it holds is only known once it has been read, and // prefill() swaps in the {{kol3}} form there if it turns out to be a list. var available = free ? [{ header: onlyHeader, id: "free", kind: "text", free: free, existing: true }] : unknown ? [{ header: onlyHeader, id: "unknown", kind: "wordlist", unknown: true, existing: true }] : PAGE_SUBSECTIONS.filter(function (s) { // "hidden" takes a section out of the picker without removing it: Werger waits for the // translation tools it belongs to, and offering a box those cannot yet fill misleads. // Werger also carries lang: "ku" -- it is a Kurdish-entry section, so it is not offered // elsewhere either. return !s.hidden && (!s.lang || s.lang === language) && (!onlyHeader || s.header === onlyHeader); }).map(function (s) { return $.extend({}, s, { existing: existing.indexOf(s.header) !== -1 }); }); if (!available.length) { $panel.text("Ji bo vê beşê guhartin tune."); return; } var $select = $("<select class='dsb-subsection-select'></select>"); if (!onlyHeader) { // Label-less on purpose: it holds the "nothing chosen yet" value without becoming a row in the // list (makeSearchable() skips a blank option that has no label), so the prompt lives in the // input's placeholder instead. $select.append($("<option value=''></option>")); } available.forEach(function (s) { // A "place" entry has no "====" heading of its own, so it is marked rather than left looking // like the subsections around it. var label = s.place ? s.header + " (ne binebeş e)" : (s.existing ? s.header + " (biguhêre)" : s.header); $select.append($("<option></option>").val(s.id).text(label)); }); var $selectWrap = makeSearchable($select, "Binebeşek hilbijêre"); if (onlyHeader) { $selectWrap.hide(); // pinned to one subsection: nothing to choose } var $fieldWrap = $("<div class='dsb-subsection-field'></div>"); var $save = $("<button type='button' class='dsb-save'></button>").text(util.msg.add); var $cancel = $("<button type='button' class='dsb-cancel'></button>").text(util.msg.cancel); var $status = $("<span class='dsb-status'></span>"); var $actions = $("<div class='dsb-form-actions'></div>").append($save, $cancel, $status); $cancel.on("click", function () { resetForm(); $panel.hide(); hideQueueIfIdle(); }); // A panel pinned to a heading on the page can rename or remove it. Bilêvkirin and the other places // have no heading of their own to change. var headingEditable = !!onlyHeader && available.length === 1 && available[0].existing && !available[0].place; var $nameWrap = null; // Only a panel pinned to one heading renames it; elsewhere the name is the picked subsection's own. var readName = function (s) { return s.header; }; if (headingEditable) { var subject = available[0]; var $nameLabel = $("<div class='dsb-adv-label'>Nav</div>"); if (subject.free) { var $nameInput = $("<input type='text' class='dsb-input dsb-heading-name'>").val(onlyHeader); readName = function () { return $.trim($nameInput.val()); }; $nameWrap = $("<div class='dsb-heading-name-wrap'></div>").append($nameLabel, $nameInput); } else { // The names the gadget knows, for a subsection of a part of speech; one this part of speech // already has is left out. var $nameSelect = $("<select class='dsb-heading-name'></select>"); var names = [onlyHeader]; PAGE_SUBSECTIONS.forEach(function (s) { if (!s.place && !s.hidden && (!s.lang || s.lang === language) && names.indexOf(s.header) === -1 && existing.indexOf(s.header) === -1) { names.push(s.header); } }); names.forEach(function (name) { $nameSelect.append($("<option></option>").val(name).text(name)); }); var $nameSearch = makeSearchable($nameSelect, "Nav"); $nameSelect.val(onlyHeader).trigger("wiktku:refresh"); readName = function () { return $nameSelect.val() || onlyHeader; }; $nameWrap = $("<div class='dsb-heading-name-wrap'></div>").append($nameLabel, $nameSearch); } var subjectDom = subsectionDom($h3, subject); if (!(subjectDom && subjectDom.hasDeeper)) { var $delete = $("<button type='button' class='dsb-delete'>Beşê jê bibe</button>"); $cancel.after($delete); $delete.on("click", function () { OO.ui.confirm("Beşa «" + subject.header + "» dê were rakirin. Dewam bike?").done(function (ok) { if (!ok) { return; } queueHeadingChange(subject, "beşa «" + subject.header + "» hate rakirin", function (pageContent) { return removeSubsection(pageContent, language, sectionType, sectionOrdinal, subject); }, function () { var dom = subsectionDom($h3, subject); if (!dom) { return null; } var $doomed = dom.$heading.add(dom.$body).addClass("dsb-preview-removed"); return function () { $doomed.removeClass("dsb-preview-removed"); }; }); }); }); } } // Renaming and removing share one queue key, so the later choice replaces the earlier one. Both run // last and bottom-up, since other queued changes find the section by the heading they change. function queueHeadingChange(s, action, transform, previewFn) { var dom = subsectionDom($h3, s); queueChange(subsectionPlaceLabel(s, language, sectionType, $h3), action, function (pageContent) { return { content: transform(pageContent), warnings: [] }; }, $save, $status, previewFn, { runLast: true, key: "heading:" + (s.free ? s.header + "|" + s.free.ordinal : sectionType + "|" + sectionOrdinal + "|" + s.header), pagePosition: dom ? headingPosition(dom.$heading) : 0 }); } function queueRename(s, newName) { queueHeadingChange(s, "beşa «" + s.header + "» bû «" + newName + "»", function (pageContent) { return renameSubsection(pageContent, language, sectionType, sectionOrdinal, s, newName); }, function () { var dom = subsectionDom($h3, s); if (!dom) { return null; } var $note = $("<div class='dsb-preview-inline dsb-preview-heading-note'></div>") .text("→ " + newName + " — nû, hîn nehatiye qeydkirin"); dom.$heading.after($note); return function () { $note.remove(); }; }); } // Undefined while the placeholder option is selected. function currentSubsection() { var id = $select.val(); return id ? available.filter(function (s) { return s.id === id; })[0] : undefined; } // One entry per "{{kol3}}" group in the current wordlist section; empty for any other kind. var groupFields = []; var $groups = null; var editAsText = false; // set when an existing wordlist section turns out to be unparseable // Whatever has been typed into each subsection's box, so switching the picker away and back restores it // instead of rebuilding an empty field. Keyed by subsection id; lastId is whichever box is on screen. var drafts = {}; var lastId = null; // Called before renderField() rebuilds, while the group fields still hold the outgoing box. function captureDraft() { if (!lastId) { return; } drafts[lastId] = imagesField ? { asText: false, images: imagesField.values() } : (groupFields.length ? { asText: false, groups: readGroups() } : { asText: editAsText, text: fieldText() }); } function restoreDraft(s, draft) { // An existing wordlist we previously fell back to raw wikitext for stays in that mode. if (draft.asText && s.kind === "wordlist") { editAsText = true; groupFields = []; renderTextArea(s, 6); } if (imagesField) { imagesField.setValues(draft.images); } else if (groupFields.length) { setGroups(s, draft.groups); } else { setFieldText(draft.text); } } // Etîmolojî is edited on a live surface (see renderChipSurface) rather than in a textarea, so every // read and write of the field goes through these two instead of touching .dsb-subsection-text. var chipSurface = null; function fieldText() { if (wergerForm) { return buildWergerBlock(wergerForm.read()); } return chipSurface ? $.trim(readChipSurface(chipSurface.get(0))) : $.trim($fieldWrap.find(".dsb-subsection-text").val() || ""); } function setFieldText(value) { if (wergerForm) { wergerForm.set(parseWergerBlock(value)); return; } if (chipSurface) { renderChipSurface(chipSurface, value || ""); var schedule = chipSurface.data("dsbSchedulePreview"); if (schedule) { schedule(); // show what the loaded etymology renders as } } else { $fieldWrap.find(".dsb-subsection-text").val(value || ""); } } // The contenteditable surface: text typed directly, template calls as chips that open the dialog, // plus start.js's etymology templates in a searchable insert menu. // A live surface where a template shows as a chip, with a menu that inserts one. Etîmolojî and // Bilêvkirin are both written this way; they differ only in which templates the menu offers. function renderTemplateEditor(s, templates) { $fieldWrap.empty(); var $select = $("<select class='dsb-subsection-select'></select>"); $select.append($("<option value=''></option>")); templates.forEach(function (t) { $select.append($("<option></option>").val(t.snippet).text(t.name + " — " + t.desc)); }); var $surface = $("<div class='dsb-tpl-surface' contenteditable='true'></div>") .attr("role", "textbox") .attr("aria-multiline", "true"); chipSurface = $surface; // Clicking the insert menu moves focus out of the surface and the browser forgets the caret, so // the last caret position inside the surface is kept and restored before inserting -- otherwise // focus() drops it back to the very start and every template lands at the beginning of the line. var savedRange = null; function rememberCaret() { var sel = window.getSelection(); if (!sel || !sel.rangeCount) { return; } var range = sel.getRangeAt(0); var root = $surface.get(0); if (range.commonAncestorContainer === root || $.contains(root, range.commonAncestorContainer)) { savedRange = range.cloneRange(); } } $surface.on("keyup mouseup focusout", rememberCaret); function restoreCaret() { if (!savedRange) { return; } var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(savedRange); } $select.on("change", function () { var snippet = $select.val(); if (!snippet) { return; } // Same substitution start.js makes: CODE is this section's language, SOURCELANG is left for // the user to fill in. snippet = snippet.replace(/CODE/g, language).replace(/SOURCELANG/g, "?"); // A pronunciation template is a bullet of its own ("* {{ku-IPA}}"), so it starts a line // rather than landing in the middle of one. var ownLine = /^\*\s/.test(snippet); if (ownLine) { snippet = $.trim(snippet.replace(/^\*\s*/, "")); } $surface.focus(); restoreCaret(); var $chip = makeTemplateChip(snippet); var piece = document.createDocumentFragment(); if (ownLine) { // The bullet, and a line of its own unless the caret already starts one. var at = caretOffset(); var before = at >= 0 ? readChipSurface($surface.get(0)).slice(0, at) : ""; piece.appendChild(document.createTextNode(!before || /\n$/.test(before) ? "* " : "\n* ")); } piece.appendChild($chip.get(0)); insertAtCaret($surface, piece); rememberCaret(); // so a second insert lands after the first, not back at the old spot $select.val(""); // Straight into the dialog: the snippet is placeholders ("?", "…") until someone fills it in. cite.openTemplateDialog(snippet).then(function (edited) { applyChipEdit($chip, edited); syncPanelWidth(); }); }); // Paste as plain text: pasted markup would otherwise become real nodes in the surface. $surface.on("paste", function (e) { var clip = e.originalEvent && e.originalEvent.clipboardData; if (!clip) { return; } e.preventDefault(); document.execCommand("insertText", false, clip.getData("text/plain")); }); // The chip the dialog was opened for, or the one that has taken its place. renderChipSurface() // rebuilds the surface from its wikitext whenever a call is typed in as text, which throws away // the element this closure is holding; without this, "Jê bibe" would remove a node that is no // longer on screen and appear to do nothing. function liveChip($chip, wikitext) { if ($.contains($surface.get(0), $chip.get(0))) { return $chip; } return $surface.find(".dsb-tpl-chip").filter(function () { return $(this).attr("data-wikitext") === wikitext; }).first(); } function applyChipEdit($chip, edited) { if (edited === null || !$chip.length) { return; } if (!$.trim(edited)) { $chip.remove(); return; } // Edited into something that is no longer one template call: let it fall back to plain text // rather than keeping a chip that lies about its contents. var call = parseTemplateCalls(edited); if (call.otherContent || call.calls.length !== 1) { $chip.replaceWith(document.createTextNode(edited)); return; } $chip.replaceWith(makeTemplateChip(edited)); } // Double-click edits the call's wikitext in place; single click opens the parameter dialog. The // click is held back briefly so the first of a double-click does not open the dialog underneath. var chipClickTimer = null; var lastChipTap = 0; var lastChipNode = null; var DOUBLE_TAP_MS = 300; // Double-tap drops the chip back to plain wikitext in the surface, rather than making the chip // itself editable: a contenteditable inside a contenteditable gives Android a selection handle and // no usable caret. As plain text the browser's own editing works, and chipifyTyped() puts the chip // back once the caret leaves the call. // A collapsed caret inside the text just inserted: at the character tapped where the browser can // say which that is, otherwise just inside the closing braces so the caret stays within the call // (which is what stops chipifyTyped putting the chip straight back). function caretRangeIn(node, event) { var range; var x = event && event.clientX; var y = event && event.clientY; if (x !== undefined && document.caretRangeFromPoint) { range = document.caretRangeFromPoint(x, y); } else if (x !== undefined && document.caretPositionFromPoint) { var pos = document.caretPositionFromPoint(x, y); if (pos) { range = document.createRange(); range.setStart(pos.offsetNode, pos.offset); } } if (range && range.startContainer === node) { range.collapse(true); return range; } var text = node.nodeValue; range = document.createRange(); range.setStart(node, /\}\}$/.test(text) ? text.length - 2 : text.length); range.collapse(true); return range; } function startInlineEdit($chip, event) { if (!$.contains($surface.get(0), $chip.get(0))) { return; // the tap timer and dblclick can both fire; only the first one has a chip left } var node = document.createTextNode($chip.attr("data-wikitext")); $chip.replaceWith(node); $surface.get(0).focus(); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(caretRangeIn(node, event)); } // Bound before the chip's own handler and stopping there, so removing a call never opens the // dialog for it on the way out. $surface.on("click", ".dsb-tpl-chip-remove", function (e) { e.preventDefault(); e.stopImmediatePropagation(); clearTimeout(chipClickTimer); lastChipNode = null; $(this).closest(".dsb-tpl-chip").remove(); syncPanelWidth(); }); $surface.on("click keydown", ".dsb-tpl-chip", function (e) { if (e.type === "keydown" && e.which !== 13 && e.which !== 32) { return; } e.preventDefault(); var $chip = $(this); clearTimeout(chipClickTimer); // A second tap on the same chip edits it inline. Timed here rather than left to "dblclick", // which mobile browsers fire unreliably or not at all for a double-tap. var now = new Date().getTime(); if (lastChipNode === this && now - lastChipTap < DOUBLE_TAP_MS) { lastChipNode = null; startInlineEdit($chip, e); return; } lastChipTap = now; lastChipNode = this; var wikitext = $chip.attr("data-wikitext"); chipClickTimer = setTimeout(function () { cite.openTemplateDialog(wikitext).then(function (edited) { applyChipEdit(liveChip($chip, wikitext), edited); syncPanelWidth(); }); }, DOUBLE_TAP_MS); }); $surface.on("dblclick", ".dsb-tpl-chip", function (e) { e.preventDefault(); clearTimeout(chipClickTimer); startInlineEdit($(this), e); }); // A long etymology cannot be read in the panel's usual 520px column, so the panel drops its // width cap once there is enough text to need it. What it will look like is shown by the // in-place render on the page itself, not by a second copy inside the form. var WIDE_AT = 240; // characters function syncPanelWidth() { var wikitext = readChipSurface($surface.get(0)); if (wikitext.length > WIDE_AT || wikitext.indexOf("\n") !== -1) { $surface.closest(".dsb-subsection-panel").addClass("dsb-panel-wide"); } } // A template typed by hand becomes a chip once typing settles, so it behaves like an inserted one: // click for the parameter dialog, double-click for its raw wikitext. var composing = false; // The caret as an offset into the surface's wikitext, which is what survives rebuilding the DOM. function caretOffset() { var sel = window.getSelection(); var root = $surface.get(0); if (!sel || !sel.rangeCount) { return -1; } var range = sel.getRangeAt(0); if (range.startContainer !== root && !$.contains(root, range.startContainer)) { return -1; // caret is somewhere else on the page } var pre = document.createRange(); pre.selectNodeContents(root); pre.setEnd(range.startContainer, range.startOffset); var holder = document.createElement("div"); holder.appendChild(pre.cloneContents()); return readChipSurface(holder).length; } // Puts the caret back at that wikitext offset. Only runs straight after a rebuild, so the surface // holds nothing but text nodes and chips. function placeCaret(offset) { var root = $surface.get(0); var range = document.createRange(); var nodes = root.childNodes; root.focus(); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var len = node.nodeType === 3 ? node.nodeValue.length : ($(node).attr("data-wikitext") || $(node).text()).length; if (offset <= len) { if (node.nodeType === 3) { range.setStart(node, offset); } else { range.setStartAfter(node); // a chip is atomic; the caret goes after it } break; } offset -= len; } if (!range.startContainer || range.startContainer === document) { range.selectNodeContents(root); } range.collapse(true); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } // True when the selection overlaps the inside of a template call. A caret resting exactly at the // closing "}}" does not count -- that is someone who has just finished typing one. function selectionTouchesCall(wikitext) { var sel = window.getSelection(); if (!sel || !sel.rangeCount) { return false; } var range = sel.getRangeAt(0); var root = $surface.get(0); if (range.startContainer !== root && !$.contains(root, range.startContainer)) { return false; } var from = caretOffset(); var to = from; if (!range.collapsed) { var pre = document.createRange(); pre.selectNodeContents(root); pre.setEnd(range.endContainer, range.endOffset); var holder = document.createElement("div"); holder.appendChild(pre.cloneContents()); to = readChipSurface(holder).length; } return parseTemplateCalls(wikitext).calls.some(function (call) { return from < call.end && to > call.start; }); } function chipifyTyped() { if (composing) { return; // never rebuild under an IME } var wikitext = readChipSurface($surface.get(0)); // Chips already hold one call each, so a higher call count means one was typed in as text. if (parseTemplateCalls(wikitext).calls.length <= $surface.find(".dsb-tpl-chip").length) { return; } var offset = caretOffset(); if (selectionTouchesCall(wikitext)) { return; // still inside a call: leave it as text until the caret moves out } renderChipSurface($surface, wikitext); savedRange = null; // the old range points at nodes that no longer exist if (offset >= 0) { placeCaret(offset); rememberCaret(); } } $surface.on("compositionstart", function () { composing = true; }); $surface.on("compositionend", function () { composing = false; schedulePreview(); }); var previewTimer = null; function schedulePreview() { clearTimeout(previewTimer); previewTimer = setTimeout(function () { chipifyTyped(); syncPanelWidth(); }, 700); } $surface.on("input", schedulePreview); $surface.data("dsbSchedulePreview", schedulePreview); $fieldWrap.append(makeSearchable($select, util.msg.addTemplate), $surface); // Bilêvkirin is written in IPA, so it gets the same character palette the wizard shows. Its // characters go in as text and its two template buttons as chips, which is what the rest of // this surface is made of. if (s.place === "pron") { $fieldWrap.append(util.buildIpaPalette({ insertText: function (text) { $surface.focus(); restoreCaret(); insertAtCaret($surface, document.createTextNode(text)); rememberCaret(); schedulePreview(); }, insertTemplate: function (snippet) { $surface.focus(); restoreCaret(); var $chip = makeTemplateChip(snippet.replace(/\|\s*\}\}$/, "}}")); insertAtCaret($surface, $chip.get(0)); rememberCaret(); cite.openTemplateDialog($chip.attr("data-wikitext")).then(function (edited) { applyChipEdit($chip, edited); syncPanelWidth(); }); } })); } } // The three "place" sections are wikitext with a shape worth spelling out, since nothing on screen // says what belongs in the box. var PLACE_PLACEHOLDERS = { "page-top": "{{h-bnr|Kelîme|Kelîmeyeke din}} an jî {{herwiha binêre/ane}}", "pos-top": "[[Wêne:Navê pelî.jpg|thumb|Binnivîs]]", "pron": "* {{ku-IPA}}" }; function renderTextArea(s, rows) { $fieldWrap.empty().append( $("<textarea class='dsb-input dsb-subsection-text'></textarea>") .attr("rows", rows) .attr("placeholder", PLACE_PLACEHOLDERS[s.place] || (s.header + "…")) ); util.initManualResize($fieldWrap); } // A translation box: the {{werger-ser}}/{{werger-bin}} pair, with the sense it covers and the // layout parameters that template accepts. The rows in between are left to the translation editor // gadget, which is what edits them on the rendered page. // Reads and writes through fieldText()/setFieldText() as wikitext, so drafts, save and the queue // need no special case beyond the append below. var wergerForm = null; var imagesField = null; function renderWergerField(s) { var inputs = {}; // Its own row class rather than .dsb-tpl-param-row: that one is styled in cite.css, which is // only on the page when wikt.cite happens to be loaded, and without it the label falls back to // sitting inline and a short one ("id") is overlapped by its own box. function row(key, label, placeholder) { inputs[key] = $("<input type='text' class='dsb-input'>").attr("placeholder", placeholder || ""); return $("<div class='dsb-werger-row'></div>").append( $("<label class='dsb-adv-label'></label>").text(label).append(inputs[key]) ); } // {{werger-ser}} also takes stûn, firehî, bgcolor, valign and align, but they only restyle the // box and its defaults are what these pages use, so the form does not offer them. They are // still in WERGER_NAMED, so a block that already sets one is read back correctly. var $main = $("<div class='dsb-werger-main'></div>").append( row("1", "Mane", "ava vexwarinê"), row("2", "Mane no", "ne mecbûrî — di parantezê de xuya dike"), row("id", "id", "ne mecbûrî — IDya tercimeyê") ); var $note = $("<p class='dsb-werger-note'></p>").text( "Qutiyeke Wergerê ya vala lê zêde dikî. Tercimeyan bi amûra wergerê dûre binivîse." ); wergerForm = { read: function () { var out = {}; $.each(inputs, function (key, $input) { var value = $.trim($input.val() || ""); if (value) { out[key] = value; } }); return out; }, set: function (params) { $.each(inputs, function (key, $input) { $input.val((params && params[key]) || ""); }); } }; $fieldWrap.empty().append($note, $main); } // Same [[Kategorî:Çavkanî]] search widget start.js's own picker uses: choosing an entry inserts // "* {{Target}}" at the cursor. The field itself stays the plain ".dsb-subsection-text" textarea // every other "kind: text" subsection already uses, so save/prefill/drafts need no changes. function renderCavkaniField(s) { var $ta = $("<textarea class='dsb-input dsb-subsection-text'></textarea>") .attr("rows", 4) .attr("placeholder", util.msg.pickOrTypeTemplate); var picker = cite.buildCategoryTemplateSearch({ placeholder: util.msg.addTemplate, describe: true, // Same param dialog Etîmolojî opens on a fresh insert: filled from the picked template's own // TemplateData, so the user adds "rûpel=", "sernav=", ... before anything lands in the field // rather than typing a bare {{Target}} and going back to fill params in by hand. onPick: function (entry) { cite.openTemplateDialog("{{" + entry.target + "}}").then(function (edited) { if (!edited) { return; // cancelled, or "Jê bibe" on a call that was never inserted } util.insertAtCursor($ta.get(0), "* " + edited + "\n"); $ta.trigger("focus"); }); } }); var $picker = $("<div class='dsb-cavkani-picker'></div>").append(picker.$element); $fieldWrap.empty().append($picker, $ta); } // One "{{kol3}}" group: its "sernav=" heading and its words. Several of these make up one section -- // "Herwiha binêre" typically carries an unheaded list plus a few headed ones. function addGroup(s, group) { var $sernav = $("<input type='text' class='dsb-input'>").attr("placeholder", "ne mecbûrî…"); // {{h-bnr}} takes bare page names, so its rows carry no ⊕: there is nowhere in the call for a // modifier to go. var words = buildWordListField(s.header + "…", { modifiers: !s.single }); var $remove = $("<a href='#' class='dsb-quick-link dsb-group-remove'>Komê rake</a>"); var $element = $("<div class='dsb-wordgroup'></div>").append( // Label beside the box, not above it: stacked, "Sernav" read as a heading for the whole // group rather than as the name of the field under it. The input is nested in the label so // clicking the word focuses the box, without needing an id per group. // A single-call list has no "sernav=" to carry, so the row is built (readGroups still reads // it) but never shown. Hidden here rather than in renderField: setGroups() rebuilds every // group on prefill, which would put a visible one back. $("<div class='dsb-tpl-param-row dsb-param-row-inline'></div>").append( $("<label class='dsb-adv-label'></label>").text("Sernav").append($sernav) ).toggle(!s.single), words.$element, $remove ); var field = { $element: $element, read: function () { return { sernav: $.trim($sernav.val()), words: words.values() }; }, set: function (g) { $sernav.val((g && g.sernav) || ""); words.setValues((g && g.words) || []); } }; $remove.on("click", function (e) { e.preventDefault(); var at = $.inArray(field, groupFields); if (at !== -1) { groupFields.splice(at, 1); } $element.remove(); updateGroupChrome(); }); field.set(group); groupFields.push(field); $groups.append($element); updateGroupChrome(); return field; } // With a single group there is nothing to tell apart, so it needs neither a remove link nor a label. function updateGroupChrome() { var many = groupFields.length > 1; $groups.toggleClass("dsb-wordgroups-many", many); $groups.find(".dsb-group-remove").toggle(many); } function readGroups() { return groupFields.map(function (field) { return field.read(); }); } // What a save writes: the same groups with each word kept once, in the order it first appears. A // list written made bi made often repeats itself, and {{kol3}} only hides that at render time. The // same word under two different headings is two different entries, so groups are deduped apart. function readGroupsForSave() { return readGroups().map(function (group) { var seen = {}; return $.extend({}, group, { // The whole row is the key, modifiers and all, so "dan<t:x>" is not the same word as // "dan". words: (group.words || []).filter(function (word) { var key = $.trim(word); if (seen[key]) { return false; } seen[key] = true; return true; }) }); }); } // Rebuilds the blocks to match what the page (or the draft) holds, always leaving at least one. function setGroups(s, groups) { groupFields = []; $groups.empty(); (groups && groups.length ? groups : [null]).forEach(function (group) { addGroup(s, group); }); } // Loads what the page already has into the form, so an edit starts from the current list rather than // from an empty one. A wordlist section we can't parse degrades to raw wikitext editing. function prefill(s) { $status.removeClass("dsb-status-error dsb-status-ok").text("Tê barkirin…"); $save.prop("disabled", true); fetchPageSubsection(language, sectionType, sectionOrdinal, s).then(function (current) { $save.prop("disabled", false); $status.text(""); if (!current) { return; } // A "place" section has no heading of its own, so whether the page already has one cannot // be seen in the DOM the way a "====" subsection can -- it is known only once its content // has been read. That is what decides "nûkirin" or "zêdekirin" in the edit summary. if (s.place) { s.existing = !!(current.images ? current.images.length : $.trim(current.text || "")); // The label was set before this content arrived, when nothing yet said one was there. $save.text(s.existing && s.kind !== "werger" ? util.msg.edit : util.msg.add); } if (current.kind === "images" && imagesField) { imagesField.setValues(current.images); return; } // A free section's field was built before its content was known: a list turns it into the // {{kol3}} form, anything else leaves it the wikitext box it already is. if (s.free && current.kind === "wordlist" && !groupFields.length) { s.kind = "wordlist"; renderWordListField(s); setGroups(s, current.groups); announceBullets(current); return; } if (current.kind === "wordlist" && groupFields.length) { setGroups(s, current.groups); announceBullets(current); return; } if (s.kind === "wordlist") { editAsText = true; groupFields = []; renderTextArea(s, 6); $status.text(s.unknown ? "Ev beş nayê naskirin — wekî wîkîtekst tê guhartin." : "Şablona vê lîsteyê nehate naskirin — wekî wîkîtekst biguhêre."); } setFieldText(current.text); }, function () { $save.prop("disabled", false); $status.addClass("dsb-status-error").text("Nikare muhtewaya heyî bixwîne."); }).then(function () { if (currentSubsection() === s) { loadedSnapshot = contentSnapshot(); } }); } // A list the page writes made bi made is about to become {{kol3}}, which is a change to the // section even when not one word of it is touched -- so the save is not held back as a no-op. var loadedFromBullets = false; function announceBullets(current) { if (!current.fromBullets) { return; } loadedFromBullets = true; $status.text("Ev lîste made bi made hatiye nivîsîn — bi qeydkirinê dibe {{kol3}}."); } // What the box held when the page's content was put in it, so a save that changed nothing queues // nothing -- renaming a heading does not also rewrite its body. var loadedSnapshot = null; function contentSnapshot() { if (imagesField) { return JSON.stringify(imagesField.values()); } return JSON.stringify(groupFields.length ? readGroups() : fieldText()); } // One "{{kol3}}" group per block, plus a way out to raw wikitext for a list no row can express -- // a shared subpage list like {{herwiha binêre/ane}}, say. function renderWordListField(s) { $fieldWrap.empty(); chipSurface = null; editAsText = false; var $addGroup = $("<a href='#' class='dsb-quick-link'>+ Komek lê zêde bike</a>"); $addGroup.on("click", function (e) { e.preventDefault(); addGroup(s, null); }); $groups = $("<div class='dsb-wordgroups'></div>"); $fieldWrap.append($groups, $addGroup); addGroup(s, null); // One call takes one flat list of names, so there is no second group to add. if (s.single) { $addGroup.hide(); } var $toRaw = $("<a href='#' class='dsb-quick-link'>Wekî wîkîtext biguhêre</a>"); $toRaw.on("click", function (e) { e.preventDefault(); var current = s.place ? wt.buildHbnrCall(loosePlaceWords(readGroupsForSave())) : subsectionBodyWikitext(language, s, readGroupsForSave()); editAsText = true; groupFields = []; $groups = null; renderTextArea(s, 3); setFieldText(current); util.initManualResize($fieldWrap); }); $fieldWrap.append($toRaw); util.initManualResize($fieldWrap); } function renderField() { var s = currentSubsection(); loadedSnapshot = null; loadedFromBullets = false; $fieldWrap.empty(); groupFields = []; $groups = null; editAsText = false; chipSurface = null; wergerForm = null; imagesField = null; $status.removeClass("dsb-status-error dsb-status-ok").text(""); // Save stays disabled (but "Betal bike" usable) until something is actually chosen. $save.prop("disabled", !s); if (!s) { $save.text(util.msg.add); lastId = null; return; // still on "Binebeşekê hilbijêre" } if (s.kind === "wordlist") { renderWordListField(s); } else if (s.kind === "images") { imagesField = util.buildImageListField({ prefix: "dsb-", rowClass: "dsb-tpl-param-row dsb-param-row-inline", inputClass: "dsb-input", addClass: "dsb-quick-link", pageName: mw.config.get("wgTitle") }); $fieldWrap.append(imagesField.$element); } else if (s.header === "Etîmolojî") { renderTemplateEditor(s, templateChoices("etymTpls", language)); } else if (s.place === "pron") { renderTemplateEditor(s, templateChoices("pronTpls", language)); } else if (s.kind === "werger") { renderWergerField(s); } else if (s.header === "Çavkanî") { renderCavkaniField(s); } else { renderTextArea(s, 3); } // Werger always adds a box, even to a section that is already there, so its button never says // "Biguhêre". $save.text(s.existing && s.kind !== "werger" ? util.msg.edit : util.msg.add); // Covers whichever branch above built the field. Idempotent, so a textarea already carrying a // grip is skipped. util.initManualResize($fieldWrap); lastId = s.id; // A draft always wins over re-reading the page: it is the newer, unsaved state. if (drafts[s.id]) { restoreDraft(s, drafts[s.id]); } else if ((s.existing || s.place) && s.kind !== "werger") { prefill(s); } } $select.on("change", function () { captureDraft(); renderField(); }); renderField(); // Back to the state a fresh open would show: no draft, no picked subsection. Called directly // rather than through the "change" handler above, so the discarded draft is never captured first. function resetForm() { drafts = {}; if (!onlyHeader) { $select.val("").trigger("wiktku:refresh"); } renderField(); } $save.on("click", function () { var s = currentSubsection(); if (!s) { return; } var newName = readName(s); if (!newName) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Navê beşê vala ye."); return; } var asText = editAsText || (s.kind !== "wordlist" && s.kind !== "images"); var effective = asText ? $.extend({}, s, { kind: "text" }) : s; var content; var hasContent; if (asText) { content = fieldText(); hasContent = !!content; } else if (s.kind === "images") { content = imagesField ? imagesField.values() : []; hasContent = content.length > 0; } else { content = readGroupsForSave(); hasContent = content.some(function (group) { return group.words.length; }); } var renaming = newName !== s.header; if (renaming) { queueRename(s, newName); } var unchanged = loadedSnapshot !== null && contentSnapshot() === loadedSnapshot && !loadedFromBullets; if (!hasContent || unchanged) { if (unchanged && !renaming) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Tiştek nehatiye guhartin."); } return; } $save.prop("disabled", true); // A Werger section holds any number of boxes, so an existing one is appended to rather than // replaced; every other kind edits the single body it owns. var appending = s.kind === "werger" && s.existing; queueChange( subsectionPlaceLabel(s, language, sectionType, $h3), s.kind === "werger" ? "qutiyeke wergerê hate zêdekirin" : "beşa «" + s.header + "» hate " + (s.existing ? "nûkirin" : "zêdekirin"), function (pageContent) { var body = content; if (appending) { var current = readPageSubsection(pageContent, language, sectionType, sectionOrdinal, effective); var existingText = current ? $.trim(current.text || "") : ""; body = existingText ? existingText + "\n\n" + content : content; } return { content: s.free ? updateFreeSection(pageContent, language, effective, body) : (s.place ? writeLoosePlace(pageContent, language, sectionType, sectionOrdinal, effective, loosePlaceBody(effective, body, language)) : (s.existing ? updatePageSubsection(pageContent, language, sectionType, sectionOrdinal, effective, body) : insertPageSubsection(pageContent, language, sectionType, sectionOrdinal, effective, body))), warnings: [] }; }, $save, $status, // Werger is a box the translation tools fill in afterwards, so there is nothing to draw for // it. A new subsection gets a block of its own at the position it will land in; one already // on the page is redrawn where it stands. s.kind === "werger" ? null : function () { // A free section is redrawn under its own heading, which is the element the panel hangs // on. if (s.free) { var restoreFree = replaceFreeSectionInPlace($h3, subsectionBodyWikitext(language, effective, content)); return function () { if (restoreFree) { restoreFree(); restoreFree = null; } }; } // A "place" section has no heading to redraw between, so it is found in the DOM by what // it renders as and replaced there. if (s.place) { var restoreLoose = replaceLoosePlaceInPlace($h3, effective, loosePlacePreviewWikitext(effective, content, language)); return function () { if (restoreLoose) { restoreLoose(); restoreLoose = null; } }; } if (!s.existing) { var $preview = buildSubsectionPreviewDom($h3, s, content, language); return function () { $preview.remove(); }; } var restore = replaceSubsectionInPlace($h3, effective.header, subsectionBodyWikitext(language, effective, content)); return function () { if (restore) { restore(); restore = null; } }; }, { key: s.free ? "section:" + s.header + "|" + s.free.ordinal : "subsection:" + sectionType + "|" + sectionOrdinal + "|" + s.header } ); }); $panel.empty().append($selectWrap, $nameWrap, $fieldWrap, $actions); } // Builds the "choose a part of speech" picker that reclassifies a section -- an unclassified // "=== Mane ===" placeholder, or a "=== Biwêj ===" that turns out to be a real part of speech. Saved via // convertSectionToPos(); lists every name from sectionNames except the one the section already is. function buildConvertPosPanel($panel, $h3, language, sectionType, sectionOrdinal) { var $select = $("<select class='dsb-convert-pos-select'></select>"); // Label-less, like the subsection picker's: it holds the "nothing chosen yet" value without showing // up as a row, so the prompt lives in the input's placeholder instead. $select.append($("<option value=''></option>")); Object.keys(sectionNames).forEach(function (name) { // The section's own part of speech is listed too, marked: picking it changes nothing about the // heading and leaves the parameters below to be edited on their own. $select.append($("<option></option>").val(name) .text(name === sectionType ? name + " (ya niha)" : name)); }); var $selectWrap = makeSearchable($select, "Li cureyekî rêzimanî bigere"); // Opens on the section's own part of speech, so editing only its parameters needs no pick. if (sectionNames[sectionType]) { $select.val(sectionType).trigger("wiktku:refresh"); } // The same card the entry wizard shows under its part-of-speech question -- gender, transitivity, // the transliteration boxes and everything behind "Tercîhên pêşketî". Shared from // wikt.edit/posparams.js, so reclassifying a section here offers exactly what creating one in the // wizard does. var card = posparams.build({ lang: language, pos: "" }); // Opened on the call the section already has, so this edits the current template rather than // starting from a blank line. setContext() rebuilds the line whenever the part of speech moves, // so the prefill is only the starting point. var spec0 = posTemplateFor(sectionType); card.setContext(language, spec0 ? spec0.name : "", /^Formeke /.test(sectionType)); fetchPageContent().then(function (pageContent) { try { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); var found = findPosTemplateLine(loc.lines, loc.posLineIndex, sectionType); if (found.index !== -1 && ($select.val() || sectionType) === sectionType) { card.setWikitext(found.text); } } catch (e) { /* an unreadable section just leaves the card as it was */ } }); $select.on("change", function () { var spec = posTemplateFor($select.val()); card.setContext(language, spec ? spec.name : "", /^Formeke /.test($select.val() || "")); }); // "gh" on its own, or "<form>-<base>" once a structural form is picked -- start.js's own composition. // convertSectionToPos() writes the template itself, so only what follows "{{pos|lang" is wanted // from the card's line. function posParams() { var m = /^\{\{[^|}]*\|[^|}]*\|?([\s\S]*)\}\}$/.exec(card.wikitext()); return m ? m[1] : ""; } var $save = $("<button type='button' class='dsb-save'></button>").text(util.msg.add); var $cancel = $("<button type='button' class='dsb-cancel'></button>").text(util.msg.cancel); var $status = $("<span class='dsb-status'></span>"); var $actions = $("<div class='dsb-form-actions'></div>").append($save, $cancel, $status); // dsb.js owns the pencil and the panel's built state, so it does the closing. $cancel.on("click", function () { $panel.trigger("dsbCancel"); hideQueueIfIdle(); }); $save.on("click", function () { var chosen = $select.val(); if (!chosen) { $status.removeClass("dsb-status-ok").addClass("dsb-status-error").text("Pêşî cureyekî rêzimanî hilbijêre."); return; } var params = posParams(); $save.prop("disabled", true); $status.removeClass("dsb-status-error").text(""); queueChange( langLabel(language), "beşa «" + sectionLabel(sectionType, $h3) + "» bû «" + chosen + "»", function (pageContent) { return { content: convertSectionToPos(pageContent, language, sectionType, sectionOrdinal, chosen, params), warnings: [] }; }, $save, $status, function () { // The heading keeps its text, which every other queued change finds the section by; a // note under it names the new one. The headword line is drawn as the save will write it. var $note = $("<div class='dsb-preview-inline dsb-preview-heading-note'></div>") .text("→ " + chosen + (params ? " (" + params + ")" : "") + " — nû, hîn nehatiye qeydkirin"); headingOuter($h3).after($note); var dropped = false; var restoreHeadword = null; fetchPageContent().then(function (pageContent) { if (dropped) { return; } var call; try { var loc = locateSection(pageContent, language, sectionType, sectionOrdinal); call = posCallLine(findPosTemplateLine(loc.lines, loc.posLineIndex, sectionType), language, chosen, params); } catch (e) { return; } restoreHeadword = replaceHeadwordInPlace($h3, call); }); return function () { dropped = true; $note.remove(); if (restoreHeadword) { restoreHeadword(); restoreHeadword = null; } }; }, // Runs after every other queued change: it renames the "Mane" heading those changes locate // themselves by, so anything edited on this section has to be applied first. { runLast: true, key: "convert:" + sectionType + "|" + sectionOrdinal, pagePosition: headingPosition($h3) } ); }); $panel.empty().append($selectWrap, card.$element, $actions); } // Attaches the "+ Beşek lê zêde bike" trigger right after a POS block's own last element. // Puts the same pencil the definitions use on every existing "====" subsection heading the gadget knows how // to edit, opening buildSubsectionPanel() pinned to that one subsection. // An editor for [[MediaWiki:Gadget-wikt.translation-adder.js]] whose changes go into this queue, under // "Kurmancî/Navdêr" like every other change to that section. Each change is tried on the page as the // queue would save it before it is queued, as the adder's own editor does. function translationEditor($h3, language, sectionType) { var place = placeLabel(language, sectionType, $h3); var $status = $("<span></span>"); var $save = $("<span></span>"); var adapter = { withCurrentText: function (callback) { fetchPageContent().then(function (content) { callback(queuedContent(content)); }, function () { adapter.error("Nikare muhtewaya rûpelê bixwîne."); }); }, // node is the line the change draws; it is highlighted while the change waits in the queue. addEdit: function (edit, node) { adapter.withCurrentText(function (text) { var next; try { next = edit.edit(text); } catch (e) { adapter.error(String(e)); return; } if (typeof next !== "string" || next === text) { return; } queueChange(place, edit.summary, edit.edit, $save, $status, function () { edit.redo(); if (node) { node.classList.add("translation-adder-new"); } return function () { if (node) { node.classList.remove("translation-adder-new"); } edit.undo(); }; }); }); }, error: function (message) { mw.notify(message, { type: "error" }); }, // The adder's "Paşve" takes back its last change. undo: function () { var last = dsbQueue.entries[dsbQueue.entries.length - 1]; if (last) { dropQueueEntry(last); } } }; return adapter; } module.exports = { translationEditor: translationEditor, buildAdvancedPanel: buildAdvancedPanel, buildSubsectionPanel: buildSubsectionPanel, buildConvertPosPanel: buildConvertPosPanel, editExample: editExample, clearQueue: clearQueue, closeAllPanels: closeAllPanels, openQueueBox: openQueueBox }; }()); // </nowiki> 3s4hlk1xsb4dz9sugmoujj4su2hl4vz Bikarhêner:Balyozbot/kontrol 2 1359386 6794070 6734715 2026-10-14T20:27:05Z Balyozbot 7867 Rojnivîsa 2026-10-14-werger-nesting 6794070 wikitext text/x-wiki == 2026-10-04-kol3-50b (koda derketinê 0) == 10:43, 4 çiriya pêşîn 2026 (UTC) <pre> + {{kol3|ku *** + |-froş *** + }} *** *** @@ -29 +31,3 @@ *** - * {{g|ku|firoş-}} *** + {{kol3|ku *** + |firoş- *** + }} *** Page [[-firoş]] saved @@ -13 +13,3 @@ *** - * [[-froşî]] *** + {{kol3|ku *** + |-froşî *** + }} *** Page [[-firoşî]] saved @@ -11 +11,3 @@ *** - * [[-firand-]] *** + {{kol3|ku *** + |-firand- *** + }} *** Page [[-firrand-]] saved @@ -11 +11,3 @@ *** - * [[-firand-]] *** + {{kol3|ku *** + |-firand- *** + }} *** Page [[-firrîn-]] saved @@ -12 +12,3 @@ *** - * {{g|ku|-firiya-}} *** + {{kol3|ku *** + |-firiya- *** + }} *** Page [[-firî-]] saved @@ -12 +12,3 @@ *** - * [[-firrîn-]] *** + {{kol3|ku *** + |-firrîn- *** + }} *** Page [[-firîn-]] saved @@ -19,4 +19,6 @@ *** - * [[-ger]] *** - * [[-gêr]] *** - * [[-kar]] *** - * [[-ker]] *** + {{kol3|ku *** + |-ger *** + |-gêr *** + |-kar *** + |-ker *** + }} *** Page [[-gar]] saved 50 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-50c (koda derketinê 0) == 10:51, 4 çiriya pêşîn 2026 (UTC) <pre> + |Ewistralya *** - * [[Entarktîka]] *** + |Entarktîka *** + }} *** Page [[Asya]] saved @@ -12 +12,3 @@ *** - * [[Augsburger]] *** + {{kol3|de *** + |Augsburger *** + }} *** Page [[Augsburg]] saved @@ -13,6 +13,8 @@ *** - * {{g|ku|Australya}} *** - * {{g|ku|Awustralya}} *** - * {{g|ku|Ewistralya}} *** - * {{g|ku|Ewustralya}} *** - * {{g|ku|Ostralya}} *** - * {{g|ku|Ustralya}} *** + {{kol3|ku *** + |Australya *** + |Awustralya *** + |Ewistralya *** + |Ewustralya *** + |Ostralya *** + |Ustralya *** + }} *** Page [[Awistralya]] saved @@ -9 +9,3 @@ *** - * [[ateria]] *** + {{kol3|qu *** + |ateria *** + }} *** Page [[Awstriya]] saved @@ -11 +11,3 @@ *** - * [[DDR]] *** + {{kol3|de *** + |DDR *** + }} *** Page [[BRD]] saved @@ -13,5 +13,7 @@ *** + {{kol3|en *** - * {{g|en|Bachelor of Arts}} *** + |Bachelor of Arts *** - * {{g|en|doctorate}} *** + |doctorate *** - * {{g|en|Specialist in Education}} *** + |Specialist in Education *** - * {{g|en|Master of Education}} *** + |Master of Education *** - * {{g|en|Master of Science}} *** + |Master of Science *** + }} *** Page [[Bachelor of Science]] saved 50 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-50d (koda derketinê 0) == 11:03, 4 çiriya pêşîn 2026 (UTC) <pre> - * {{g|en|baby boomer}} *** - * {{g|en|echo boomer}} *** - * {{g|en|millennial}} *** - * {{g|en|zoomer}} *** + {{kol3|en *** + |baby buster *** + |baby boomer *** + |echo boomer *** + |millennial *** + |zoomer *** + }} *** Page [[Generation X]] saved @@ -17,9 +17,11 @@ *** + {{kol3|ku *** - * [[Gola Qezwînê]] *** + |Gola Qezwînê *** - * [[Deryaya Qezwînê]] *** + |Deryaya Qezwînê *** - * [[Deryaçeya Qezwînê]] *** + |Deryaçeya Qezwînê *** - * [[Gola Xezerê]] *** + |Gola Xezerê *** - * [[Deryaçeya Xezerê]] *** + |Deryaçeya Xezerê *** - * [[Deryaya Xezerê]] *** + |Deryaya Xezerê *** - * [[Gola Mazenderanê]] *** + |Gola Mazenderanê *** - * [[Deryaçeya Mazenderanê]] *** + |Deryaçeya Mazenderanê *** - * [[Deryaya Mazenderanê]] *** + |Deryaya Mazenderanê *** + }} *** Page [[Gola Hezarê]] saved @@ -17,9 +17,11 @@ *** + {{kol3|ku *** - * [[Gola Qezwînê]] *** + |Gola Qezwînê *** - * [[Deryaya Qezwînê]] *** + |Deryaya Qezwînê *** - * [[Deryaçeya Qezwînê]] *** + |Deryaçeya Qezwînê *** - * [[Gola Hezarê]] *** + |Gola Hezarê *** - * [[Deryaçeya Hezarê]] *** + |Deryaçeya Hezarê *** - * [[Deryaya Hezarê]] *** + |Deryaya Hezarê *** - * [[Gola Xezerê]] *** + |Gola Xezerê *** - * [[Deryaçeya Xezerê]] *** + |Deryaçeya Xezerê *** - * [[Deryaya Xezerê]] *** + |Deryaya Xezerê *** + }} *** Page [[Gola Mazenderanê]] saved 50 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-50e (koda derketinê 0) == 11:09, 4 çiriya pêşîn 2026 (UTC) <pre> - * [[UT]] *** - * [[VA]] *** - * [[VT]] *** - * [[WA]] *** - * [[WI]] *** - * [[WV]] *** - * [[WY]] *** + {{kol3|en *** + |AL *** + |AR *** + |AZ *** + |CA *** + |CO *** + |CT *** + |DE *** + |FL *** + |GA *** + |IA *** + |ID *** + |IL *** + |KS *** + |KY *** + |LA *** + |MA *** + |MD *** + |ME *** + |MI *** + |MN *** + |MO *** + |MS *** + |MT *** + |NC *** + |ND *** + |NE *** + |NH *** + |NJ *** + |NM *** + |NV *** + |NY *** + |OH *** + |OK *** + |OR *** + |OR *** + |PA *** + |RI *** + |SC *** + |SD *** + |TN *** + |TX *** + |UT *** + |VA *** + |VT *** + |WA *** + |WI *** + |WV *** + |WY *** + }} *** Page [[MA]] saved 50 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-50f (koda derketinê 0) == 11:22, 4 çiriya pêşîn 2026 (UTC) <pre> + |paquistaní *** + }} *** Page [[Pakistán]] saved @@ -11,7 +11,9 @@ *** + {{kol3|de *** - * {{g|de|Cayennepfeffer}} *** + |Cayennepfeffer *** - * {{g|de|Pfefferkuchen}} *** + |Pfefferkuchen *** - * {{g|de|Pfeffermühle}} *** - * {{g|de|pfeffern}} *** - * {{g|de|Pfeffernuss}} *** + |Pfeffermühle *** + |pfeffern *** + |Pfeffernuss *** - * {{g|de|Pfefferstreuer}} *** + |Pfefferstreuer *** - * {{g|de|wo der Pfeffer wächst}} *** + |wo der Pfeffer wächst *** + }} *** Page [[Pfeffer]] saved @@ -15,5 +15,7 @@ *** - * [[platonî]] *** - * [[platonîk]] *** - * [[platonîkî]] *** - * [[platonîst]] *** - * [[platonîzm]] *** + {{kol3|ku *** + |platonî *** + |platonîk *** + |platonîkî *** + |platonîst *** + |platonîzm *** + }} *** Page [[Platon]] saved @@ -8 +8,3 @@ *** - * [[Pogge]] *** + {{kol3|nds *** + |Pogge *** + }} *** Page [[Pogg]] saved @@ -13 +13,3 @@ *** + {{kol3|de *** - * [[Außenpolitik]], [[Energiepolitik]], [[Innenpolitik]], [[Politikwissenschaft]], [[Kriegspolitik]] *** + |Außenpolitik,Energiepolitik,Innenpolitik,Politikwissenschaft,Kriegspolitik *** + }} *** Page [[Politik]] saved @@ -15 +15,3 @@ *** - * [[Großprojekt]] *** + {{kol3|de *** + |Großprojekt *** + }} *** Page [[Projekt]] saved 50 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-50g (koda derketinê 0) == 11:30, 4 çiriya pêşîn 2026 (UTC) <pre> - * [[tonganîtî]] *** - * [[tongayî]] *** - * [[tongayîtî]] *** - * [[tongî]] *** - * [[tongîtî]] *** + {{kol3|ku *** + |tonganî *** + |tonganîtî *** + |tongayî *** + |tongayîtî *** + |tongî *** + |tongîtî *** + }} *** Page [[Tonga]] saved @@ -13 +13,3 @@ *** - * [[Brücke]] *** + {{kol3|de *** + |Brücke *** + }} *** Page [[Tunnel]] saved @@ -12 +12,3 @@ *** - * [[Tyrannei]] *** + {{kol3|de *** + |Tyrannei *** + }} *** Page [[Tyrann]] saved @@ -13 +13,3 @@ *** - * [[Tänzerin]] *** + {{kol3|de *** + |Tänzerin *** + }} *** Page [[Tänzer]] saved @@ -13,2 +13,4 @@ *** - * [[Türkçe]] *** - * [[Türkiye]] *** + {{kol3|tr *** + |Türkçe *** + |Türkiye *** + }} *** Page [[Türk]] saved @@ -11 +11,3 @@ *** - * [[M.E.Y.]] *** + {{kol3|en *** + |M.E.Y. *** + }} *** Page [[UAE]] saved @@ -15 +15,3 @@ *** - * [[Kontur]] *** + {{kol3|de *** + |Kontur *** + }} *** Page [[Umriss]] saved 50 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-all (koda derketinê 0) == 11:41, 4 çiriya pêşîn 2026 (UTC) <pre> + |Mehmed *** + |Miheme *** + |Mihemed *** + |Memed *** + |Mihê *** + |Miho *** + |Mihyedîn *** + }} *** Page [[Mehma]] saved @@ -16,2 +16,4 @@ *** + {{kol3|de *** - * [[metallhaltig]] *** + |metallhaltig *** - * [[metallisch]] *** + |metallisch *** + }} *** Page [[Metall]] saved @@ -16,3 +16,5 @@ *** + {{kol3|en *** - * [[Old Armenian]] *** + |Old Armenian *** - * [[Grabar]] *** + |Grabar *** - * [[Liturgical Armenian]] *** + |Liturgical Armenian *** + }} *** Page [[Middle Armenian]] saved @@ -11 +11,3 @@ *** + {{kol3|de *** - * [[Musiklehrerin]] *** + |Musiklehrerin *** + }} *** Page [[Musiklehrer]] saved @@ -15 +15,3 @@ *** - * [[niveau]] *** + {{kol3|de *** + |niveau *** + }} *** Page [[Niveau]] saved @@ -14 +14,3 @@ *** - * [[Patriotin]] *** + {{kol3|de *** + |Patriotin *** + }} *** Page [[Patriot]] saved @@ -9 +9,3 @@ *** + {{kol3|de *** - * [[patriotismus]] *** + |patriotismus *** + }} *** Page [[Patriotismus]] saved Stopped: Bikarhêner:Balyozbot/rawestîne does not say 'erê'. 29 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-kol3-all-2 (koda derketinê 143) == 13:35, 4 çiriya pêşîn 2026 (UTC) <pre> + |gernasbûn<crz:navdêr> *** - |gernaskirin<pos:navdêr> *** + |gernaskirin<crz:navdêr> *** - |gernasbûyî<pos:rengdêr> *** + |gernasbûyî<crz:rengdêr> *** - |gernaskirî<pos:rengdêr> *** + |gernaskirî<crz:rengdêr> *** Page [[gernas]] saved @@ -10,2 +10,4 @@ *** - * [[gerreg]] *** - * [[geye]] *** + {{kol3|sdh *** + |gerreg *** + |geye *** + }} *** Page [[gerre]] saved @@ -14,5 +14,7 @@ *** - * [[gerrvanî]] *** - * [[gervanîtî]] *** - * [[gervantî]] *** - * [[gervanîtî]] *** - * [[gervantî]] *** + {{kol3|ku *** + |gerrvanî *** + |gervanîtî *** + |gervantî *** + |gervanîtî *** + |gervantî *** + }} *** Page [[gervanî]] saved @@ -12,2 +12,2 @@ *** - |gerwerzî kirin<pos:lêker> *** + |gerwerzî kirin<crz:lêker> *** - |gerwerzîkirin<pos:navdêr> *** + |gerwerzîkirin<crz:navdêr> *** Page [[gerwerzî]] saved @@ -55 +55 @@ *** - |ji bo xweveşartinê li qulekê gerîn<q:biwêj> *** + |ji bo xweveşartinê li qulekê gerîn<crz:biwêj> *** Page [[gerîn]] saved @@ -23,5 +23,7 @@ *** - * [[cesedî]] *** - * [[bcesednas]] *** - * [[cesednasî]] *** + {{kol3|ku *** + |cesedî *** + |bcesednas *** + |cesednasî *** - * [[cesedparêz]] *** + |cesedparêz *** - * [[cesedparêzî]] *** + |cesedparêzî *** + }} *** Terminated </pre> == 2026-10-04-kol3-count (koda derketinê 0) == 13:39, 4 çiriya pêşîn 2026 (UTC) <pre> pages on the list: 0 </pre> == 2026-10-04-sernav-dump (koda derketinê 0) == 16:40, 4 çiriya pêşîn 2026 (UTC) <pre> /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Optional" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Union" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Dict" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "List" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Tuple" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: _ConfigurationDeprecationWarning: "maxlag" present in your user-config.py is deprecated since 11.8.0 and no longer a supported configuration variable and should be removed; use write_maxlag instead. Please inform the maintainers if you depend on it. _check_user_config_types(_exec_globals, _public_globals, _imports) Page [[Bikarhêner:Balyozbot/sernavên nenas]] saved 1035951 pages; unknown: 996 titles, 3529 uses; Formeke: 15; level 2 without ziman: 22 </pre> == 2026-10-04-sernav-dump-2 (koda derketinê 0) == 17:21, 4 çiriya pêşîn 2026 (UTC) <pre> /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Optional" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Union" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Dict" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "List" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Tuple" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: _ConfigurationDeprecationWarning: "maxlag" present in your user-config.py is deprecated since 11.8.0 and no longer a supported configuration variable and should be removed; use write_maxlag instead. Please inform the maintainers if you depend on it. _check_user_config_types(_exec_globals, _public_globals, _imports) Page [[Bikarhêner:Balyozbot/sernavên nenas]] saved 1035951 pages; unknown: 973 titles, 2740 uses; Formeke: 16; level 2 without ziman: 22 </pre> == 2026-10-04-kol3-dump (koda derketinê 0) == 17:45, 4 çiriya pêşîn 2026 (UTC) <pre> + |kulzikî bûn *** + |kulzikîkirin *** - * {{g|ku|kulzikî kirin}} *** + |kulzikî kirin *** - * {{g|ku|kulzikbûyî}} *** - * {{g|ku|kulzikkirî}} *** - * {{g|ku|kulziktî}} *** + |kulzikbûyî *** + |kulzikkirî *** + |kulziktî *** + }} *** Page [[kulzik]] saved @@ -17,5 +17,7 @@ *** - * {{g|ku|tîsk}} *** - * {{g|ku|tîskê}} *** - * {{g|ku|tenek}} *** - * {{g|ku|hinek}} *** - * {{g|ku|bîskek}} *** + {{kol3|ku *** + |tîsk *** + |tîskê *** + |tenek *** + |hinek *** + |bîskek *** + }} *** Page [[tîsik]] saved @@ -9 +9 @@ *** - # [[şêlan]], [[dizî ]], [[birin]]a tiştekê/î bêyî hay û rizadiliya xwedana/ê wî tiştî. *** + # [[şêlan]], [[dizî]] , [[birin]]a tiştekê/î bêyî hay û rizadiliya xwedana/ê wî tiştî. *** @@ -14 +14 @@ *** - {{ku-tewîn-nav/nû|mê}} *** + {{ku-tewîn-nav|mê}} *** @@ -18,3 +18,3 @@ *** - |qavî kirin<pos:lêker> *** + |qavî kirin<crz:lêker> *** - |qavîkirin<pos:navdêr> *** + |qavîkirin<crz:navdêr> *** - |qavîkirî<pos:rengdêr> *** + |qavîkirî<crz:rengdêr> *** Page [[qavî]] saved @@ -8 +8,3 @@ *** - * [[oxeye daisy]] *** + {{kol3|en *** + |oxeye daisy *** + }} *** Page [[ox-eye daisy]] saved @@ -8 +8,3 @@ *** + {{kol3|en *** - * [[ox-eye daisy]] *** + |ox-eye daisy *** + }} *** Page [[oxeye daisy]] saved 4672 page(s) saved. See kol3_report.md. </pre> == 2026-10-04-sernav-fix (koda derketinê 0) == 17:58, 4 çiriya pêşîn 2026 (UTC) <pre> Page [[dewirandin]] saved @@ -24,3 +24,2 @@ *** + ==== Bi zaravayên din ==== *** + * {{Z|ckb}}: [[derêner]], [[derhêner]] *** - ====Bi soranî==== *** - * [[derêner]] *** - * [[derhêner]] *** Page [[dîrektor]] saved @@ -14 +14 @@ *** - *''Bilanî'': (''[[devokî]]'') [[bihewê]], [[bihewit]], [[bihewît]], [[bihewitin]], [[bihewîtin]] *** + * ''Bilanî'': (''[[devokî]]'') [[bihewê]], [[bihewit]], [[bihewît]], [[bihewitin]], [[bihewîtin]] *** @@ -28,5 +28,7 @@ *** - ====Demên din==== *** - * [[dihewe]] *** - * [[hewî]], [[hewiya]] *** - * [[hewiye]], [[hewiyaye]] *** - * [[hewîbû]], [[hewiyabû]] *** + ==== Herwiha binêre ==== *** + {{kol3|ku|sernav=Demên din *** + |dihewe *** + |hewî,hewiya *** + |hewiye,hewiyaye *** + |hewîbû,hewiyabû *** + }} *** Page [[bihewe]] saved @@ -19,2 +19,2 @@ *** - ==== Bi soranî ==== *** - * [[darî udî]] *** + ==== Bi zaravayên din ==== *** + * {{Z|ckb}}: [[darî udî]] *** Page [[udî]] saved @@ -12 +12 @@ *** - *[[ez te dihebînim]] *** + * [[ez te dihebînim]] *** @@ -17,2 +17,2 @@ *** - ==== Bi soranî ==== *** + ==== Bi zaravayên din ==== *** - * [[من حه‌زم لێته]]، [[من عاشقی تۆم]]، [[من عاشقتم]]، [[زۆرم خۆشەوێی]] *** + * {{Z|ckb}}: [[من حه‌زم لێته]], [[من عاشقی تۆم]], [[من عاشقتم]], [[زۆرم خۆشەوێی]] *** Page [[te dihebînim]] saved @@ -19,4 +19,6 @@ *** - ==== Demên din ==== *** - * [[hebim]] *** - * [[hebûm]] *** - * [[hebûme]] *** + ==== Herwiha binêre ==== *** + {{kol3|ku|sernav=Demên din *** + |hebim *** + |hebûm *** + |hebûme *** + }} *** Page [[heme]] saved 155 page(s) saved. See sernav_fix_report.md. </pre> == 2026-10-04-nivisen-alternatif (koda derketinê 0) == 18:13, 4 çiriya pêşîn 2026 (UTC) <pre> _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Tuple" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: _ConfigurationDeprecationWarning: "maxlag" present in your user-config.py is deprecated since 11.8.0 and no longer a supported configuration variable and should be removed; use write_maxlag instead. Please inform the maintainers if you depend on it. _check_user_config_types(_exec_globals, _public_globals, _imports) Working on 'almenaŭ' Working on 'ambaŭ' Working on 'ankaŭ' Working on 'ankoraŭ' Working on 'anstataŭ' Working on 'apenaŭ' Working on 'aĉeti' Working on 'aĝo' Working on 'aŭ' Working on 'aŭdi' Working on 'aŭskulti' Working on 'baldaŭ' Working on 'buŝo' Working on 'daŭro' Working on 'dolĉa' Working on 'hieraŭ' Working on 'hodiaŭ' Working on 'kontraŭ' Working on 'kuraĝa' Working on 'kvazaŭ' Working on 'laŭ' Working on 'manĝi' Working on 'morgaŭ' Working on 'naŭ' Working on 'preskaŭ' Working on 'serĉi' Working on 'vojaĝi' Working on 'ĉar' Working on 'ĉe' Working on 'ĉef-' Working on 'ĉevalo' Working on 'ĉi' Working on 'ĉia' Working on 'ĉiam' Working on 'ĉielo' Working on 'ĉio' Working on 'ĉirkaŭ' Working on 'ĉiu' Working on 'ĉu' Working on 'ĝis' Working on 'ĵeti' Working on 'ĵus' Working on 'ŝanĝi' 43 read operations Execution time: Read operation time: 0.0 seconds Script terminated successfully. </pre> == 2026-10-04-nivisen-alternatif-l3 (koda derketinê 0) == 18:16, 4 çiriya pêşîn 2026 (UTC) <pre> /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "List" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Tuple" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: _ConfigurationDeprecationWarning: "maxlag" present in your user-config.py is deprecated since 11.8.0 and no longer a supported configuration variable and should be removed; use write_maxlag instead. Please inform the maintainers if you depend on it. _check_user_config_types(_exec_globals, _public_globals, _imports) Page [[almenaŭ]] saved Page [[ambaŭ]] saved Page [[ankaŭ]] saved Page [[ankoraŭ]] saved Page [[anstataŭ]] saved Page [[apenaŭ]] saved Page [[aĉeti]] saved Page [[aĝo]] saved Page [[aŭ]] saved Page [[aŭdi]] saved Page [[aŭskulti]] saved Page [[baldaŭ]] saved Page [[buŝo]] saved Page [[daŭro]] saved Page [[dolĉa]] saved Page [[hieraŭ]] saved Page [[hodiaŭ]] saved Page [[kontraŭ]] saved Page [[kuraĝa]] saved Page [[kvazaŭ]] saved Page [[laŭ]] saved Page [[manĝi]] saved Page [[morgaŭ]] saved Page [[naŭ]] saved Page [[preskaŭ]] saved Page [[serĉi]] saved Page [[vojaĝi]] saved Page [[ĉar]] saved Page [[ĉe]] saved Page [[ĉef-]] saved Page [[ĉevalo]] saved Page [[ĉi]] saved Page [[ĉia]] saved Page [[ĉiam]] saved Page [[ĉielo]] saved Page [[ĉio]] saved Page [[ĉirkaŭ]] saved Page [[ĉiu]] saved Page [[ĉu]] saved Page [[ĝis]] saved Page [[ĵeti]] saved Page [[ĵus]] saved Page [[ŝanĝi]] saved 43 page(s) saved </pre> == 2026-10-14-werger-nesting (koda derketinê 0) == 20:27, 14 çiriya pêşîn 2026 (UTC) <pre> /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Optional" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Union" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Dict" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "List" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: UserWarning: Configuration variable "Tuple" is defined in your user-config.py but unknown. It can be a misspelled one or a variable that is no longer supported. _check_user_config_types(_exec_globals, _public_globals, _imports) /data/project/balyozbottest/pwbvenv/lib/python3.11/site-packages/pywikibot/config.py:1056: _ConfigurationDeprecationWarning: "maxlag" present in your user-config.py is deprecated since 11.8.0 and no longer a supported configuration variable and should be removed; use write_maxlag instead. Please inform the maintainers if you depend on it. _check_user_config_types(_exec_globals, _public_globals, _imports) Page [[Bikarhêner:Balyozbot/zimanên di bin zimanan de]] saved 1035951 pages; 863 nested pairs, 29326 lines </pre> 2tq91l0gdwlchstx7xkiooa50h0cz5u fall behind 0 1359431 6793950 6793927 2026-10-14T17:14:20Z Wikihez 7344 /* Lêker */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re) 6793950 wikitext text/x-wiki == {{ziman|en}} == === Lêker === {{lêker|en}} # {{g|ku|li dû hevalan man}} j9485dsj5e81ve8p8awonvjx9zuhtey Gotûbêj:dengaz 1 1359435 6793962 6793944 2026-10-14T18:17:51Z Wikihez 7344 /* Şibhe li ser peyvê heye */ Bersiv 6793962 wikitext text/x-wiki == Şibhe li ser peyvê heye == Gelo peyveke bi vî rengî heye? [[Bikarhêner:Bibliophile|Bibliophile]] ([[Gotûbêja bikarhêner:Bibliophile|gotûbêj]]) 18:47, 13 çiriya pêşîn 2026 (UTC) :@[[Bikarhêner:Bibliophile|Bibliophile]] Min tenê du çavkanî dîtin. Xuya ye botek ji ferhenga Destî kopî kiriye. Eger çavkaniya siseyan nayê dîtin, divê were jêbirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:17, 14 çiriya pêşîn 2026 (UTC) e2avkwvbwll7o3vc7tttxdolabf16fv Bikarhêner:Wikihez/Gadget-test/translation-adder.js 2 1359437 6793965 2026-10-14T18:20:17Z Wikihez 7344 Test copy of the translation adder gadget (fork of en.wiktionary's TranslationAdder) 6793965 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Edits pile up in * a box at the top left and are saved together. Forked from en.wiktionary's * [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * The form reads only boxes from [[Modul:werger]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; langName.textContent = lang && codeToName[lang] ? " (" + codeToName[lang] + ")" : ""; if (!preserve) autoTransliterate(); var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } // Completes a Kurmancî language name when only one language starts with what was typed. function langAutoFill(e) { var key = e.keyCode; if ((key >= 33 && key <= 40) || key == 8 || key == 46 || key == 27 || key == 16) { return; } var t = this, prefix = t.value.toLowerCase(); if (prefix.length < 2 || codeToName[prefix]) { return; } var found = null; for (var name in nameToCode) { if (name.indexOf(prefix) === 0) { if (found && nameToCode[found] !== nameToCode[name]) return; if (!found || name.length < found.length) found = name; } } if (found) { var oldLength = t.value.length; t.value = found; t.setSelectionRange(oldLength, found.length); } } langInput.onkeyup = langAutoFill; langInput.onblur = function () { updateFieldVisibility.call(langInput); }; window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langInput, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W-") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + content.substr(content.indexOf(':') + 1); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + content.substr(content.indexOf(':') + 1); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + content.substr(content.indexOf(':') + 1); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // Adds the forms to the boxes in root (an element or jQuery object). function attach(root) { return loadNames().then(function () { TranslationAdders(new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, compareNames: compareNames }; module.exports = window.wikt.translationAdder; if (mw.config.get('wgAction') == 'view' && mw.util.getParamValue("printable") === null && mw.util.getParamValue("diff") === null && mw.util.getParamValue("oldid") === null) { mw.hook('wikipage.content').add(function ($content) { if ($content.find('table.translations').length) { attach($content); } }); } // </nowiki> t3uc9ncbg9uvvv0b20zj2r35e3c2d5e 6793993 6793965 2026-10-14T18:25:09Z Wikihez 7344 Cut the language label from the rendered line by DOM, as the preview HTML has colons in attributes 6793993 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Edits pile up in * a box at the top left and are saved together. Forked from en.wiktionary's * [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * The form reads only boxes from [[Modul:werger]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; while (div.firstChild) { var node = div.firstChild; var i = node.nodeType === Node.TEXT_NODE ? node.data.indexOf(':') : -1; if (i !== -1) { node.data = node.data.slice(i + 1); break; } div.removeChild(node); } return div.innerHTML; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; langName.textContent = lang && codeToName[lang] ? " (" + codeToName[lang] + ")" : ""; if (!preserve) autoTransliterate(); var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } // Completes a Kurmancî language name when only one language starts with what was typed. function langAutoFill(e) { var key = e.keyCode; if ((key >= 33 && key <= 40) || key == 8 || key == 46 || key == 27 || key == 16) { return; } var t = this, prefix = t.value.toLowerCase(); if (prefix.length < 2 || codeToName[prefix]) { return; } var found = null; for (var name in nameToCode) { if (name.indexOf(prefix) === 0) { if (found && nameToCode[found] !== nameToCode[name]) return; if (!found || name.length < found.length) found = name; } } if (found) { var oldLength = t.value.length; t.value = found; t.setSelectionRange(oldLength, found.length); } } langInput.onkeyup = langAutoFill; langInput.onblur = function () { updateFieldVisibility.call(langInput); }; window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langInput, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W-") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // Adds the forms to the boxes in root (an element or jQuery object). function attach(root) { return loadNames().then(function () { TranslationAdders(new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, compareNames: compareNames }; module.exports = window.wikt.translationAdder; if (mw.config.get('wgAction') == 'view' && mw.util.getParamValue("printable") === null && mw.util.getParamValue("diff") === null && mw.util.getParamValue("oldid") === null) { mw.hook('wikipage.content').add(function ($content) { if ($content.find('table.translations').length) { attach($content); } }); } // </nowiki> jx4mi9qarbatjtgwqhds74mt5mpwhqr 6794000 6793993 2026-10-14T18:29:56Z Wikihez 7344 Cut the language label with a Range, as Parsoid wraps it in spans 6794000 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Edits pile up in * a box at the top left and are saved together. Forked from en.wiktionary's * [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * The form reads only boxes from [[Modul:werger]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; langName.textContent = lang && codeToName[lang] ? " (" + codeToName[lang] + ")" : ""; if (!preserve) autoTransliterate(); var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } // Completes a Kurmancî language name when only one language starts with what was typed. function langAutoFill(e) { var key = e.keyCode; if ((key >= 33 && key <= 40) || key == 8 || key == 46 || key == 27 || key == 16) { return; } var t = this, prefix = t.value.toLowerCase(); if (prefix.length < 2 || codeToName[prefix]) { return; } var found = null; for (var name in nameToCode) { if (name.indexOf(prefix) === 0) { if (found && nameToCode[found] !== nameToCode[name]) return; if (!found || name.length < found.length) found = name; } } if (found) { var oldLength = t.value.length; t.value = found; t.setSelectionRange(oldLength, found.length); } } langInput.onkeyup = langAutoFill; langInput.onblur = function () { updateFieldVisibility.call(langInput); }; window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langInput, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W-") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // Adds the forms to the boxes in root (an element or jQuery object). function attach(root) { return loadNames().then(function () { TranslationAdders(new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, compareNames: compareNames }; module.exports = window.wikt.translationAdder; if (mw.config.get('wgAction') == 'view' && mw.util.getParamValue("printable") === null && mw.util.getParamValue("diff") === null && mw.util.getParamValue("oldid") === null) { mw.hook('wikipage.content').add(function ($content) { if ($content.find('table.translations').length) { attach($content); } }); } // </nowiki> b3oyujmjkte4jyw00f4r48wkbq5d1mv 6794027 6794000 2026-10-14T19:03:30Z Wikihez 7344 Write {{W}} when the other wiktionary has no entry, as en.wiktionary writes {{t}} 6794027 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Edits pile up in * a box at the top left and are saved together. Forked from en.wiktionary's * [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; langName.textContent = lang && codeToName[lang] ? " (" + codeToName[lang] + ")" : ""; if (!preserve) autoTransliterate(); var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } // Completes a Kurmancî language name when only one language starts with what was typed. function langAutoFill(e) { var key = e.keyCode; if ((key >= 33 && key <= 40) || key == 8 || key == 46 || key == 27 || key == 16) { return; } var t = this, prefix = t.value.toLowerCase(); if (prefix.length < 2 || codeToName[prefix]) { return; } var found = null; for (var name in nameToCode) { if (name.indexOf(prefix) === 0) { if (found && nameToCode[found] !== nameToCode[name]) return; if (!found || name.length < found.length) found = name; } } if (found) { var oldLength = t.value.length; t.value = found; t.setSelectionRange(oldLength, found.length); } } langInput.onkeyup = langAutoFill; langInput.onblur = function () { updateFieldVisibility.call(langInput); }; window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langInput, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // Adds the forms to the boxes in root (an element or jQuery object). function attach(root) { return loadNames().then(function () { TranslationAdders(new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, compareNames: compareNames }; module.exports = window.wikt.translationAdder; if (mw.config.get('wgAction') == 'view' && mw.util.getParamValue("printable") === null && mw.util.getParamValue("diff") === null && mw.util.getParamValue("oldid") === null) { mw.hook('wikipage.content').add(function ($content) { if ($content.find('table.translations').length) { attach($content); } }); } // </nowiki> 1go7k3ykabnhd7cfwd7nftu4quwqcxx 6794031 6794027 2026-10-14T19:20:20Z Wikihez 7344 Translation adder: language dropdown, code shown as its name, editor passed in by DefSideBoxes 6794031 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it, then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } var starts = [], contains = []; Object.keys(nameToCode).forEach(function (name) { var code = nameToCode[name]; var i = name.indexOf(term); if (code === term || i === -1) { return; } (i === 0 ? starts : contains).push(code); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } return exact.concat(starts.sort(byName), contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input, menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) autoTransliterate(); var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 9mxapz0ed2kh7ykcl600yn6j8glfidu 6794040 6794031 2026-10-14T19:22:58Z Wikihez 7344 Language search ignores diacritics and offers shorter names first 6794040 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input, menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) autoTransliterate(); var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> rzdc94aor4sn215lp25elw5god7zjfw 6794042 6794040 2026-10-14T19:33:32Z Wikihez 7344 Translation adder: automatic nesting by code, line editing (✎ ×), highlighted pending lines 6794042 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input, menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var first = $.parseHTML(html)[0]; return first && first.children[0] ? first.children[0].innerHTML : ''; }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { nodes.push(node); } return nodes; } // ✎ (edit the wikitext after "Language:") and × (remove the line) on one translation line. function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var title = ucfirst(codeToName[getLangCode(line)] || name); var $edit = $('<a href="#">✎</a>').attr('title', 'Vê rêzê biguherîne'); var $remove = $('<a href="#">×</a>').attr('title', 'Vê rêzê rake'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', $edit, ' ', $remove); var $form = null; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function closeForm() { if ($form) { $form.remove(); $form = null; } } $edit.on('click', function (e) { e.preventDefault(); if ($form) { return closeForm(); } editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } var $input = $('<input type="text" size="60">').val(found.rest); var $status = $('<span>'); $form = $('<form class="translation-adder-line-form">').append($input, ' ', $('<input type="submit">').val('Pêşdîtin'), ' ', $('<button type="button">').text('Betal').on('click', closeForm), ' ', $status); $tools.after($form); $input.trigger('focus'); $form.on('submit', function (ev) { ev.preventDefault(); var rest = $input.val().trim(); if (!rest) { return $status.text("Ji bo rakirinê × bi kar bîne."); } $status.text('Tê barkirin…'); render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line).filter(function (n) { return n !== $tools[0]; }); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); closeForm(); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); }, edit: function (current) { var at = findLine(current, line); if (!at) { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } return current.substring(0, at.restStart) + rest + current.substring(at.end); }, summary: '±' + title }, line); }, function () { $status.text('Têkilî bi serverê re çênebû.'); }); }); }); }); $remove.on('click', function (e) { e.preventDefault(); if (line.querySelector('ul, dl')) { return editor.error("Pêşî rêzên di bin " + title + " de rake."); } editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); }); this.$tools = $tools; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root. function setLineEditing(root, editor, on) { return loadNames().then(function () { $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor || new Editor(), this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 3v2mi5iepwssbvw4hr648j0ghtlw1nn 6794047 6794042 2026-10-14T19:36:33Z Wikihez 7344 Line preview without its paragraph; removed lines outlined red 6794047 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input, menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { nodes.push(node); } return nodes; } // ✎ (edit the wikitext after "Language:") and × (remove the line) on one translation line. function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var title = ucfirst(codeToName[getLangCode(line)] || name); var $edit = $('<a href="#">✎</a>').attr('title', 'Vê rêzê biguherîne'); var $remove = $('<a href="#">×</a>').attr('title', 'Vê rêzê rake'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', $edit, ' ', $remove); var $form = null; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function closeForm() { if ($form) { $form.remove(); $form = null; } } $edit.on('click', function (e) { e.preventDefault(); if ($form) { return closeForm(); } editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } var $input = $('<input type="text" size="60">').val(found.rest); var $status = $('<span>'); $form = $('<form class="translation-adder-line-form">').append($input, ' ', $('<input type="submit">').val('Pêşdîtin'), ' ', $('<button type="button">').text('Betal').on('click', closeForm), ' ', $status); $tools.after($form); $input.trigger('focus'); $form.on('submit', function (ev) { ev.preventDefault(); var rest = $input.val().trim(); if (!rest) { return $status.text("Ji bo rakirinê × bi kar bîne."); } $status.text('Tê barkirin…'); render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line).filter(function (n) { return n !== $tools[0]; }); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); closeForm(); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); }, edit: function (current) { var at = findLine(current, line); if (!at) { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } return current.substring(0, at.restStart) + rest + current.substring(at.end); }, summary: '±' + title }, line); }, function () { $status.text('Têkilî bi serverê re çênebû.'); }); }); }); }); $remove.on('click', function (e) { e.preventDefault(); if (line.querySelector('ul, dl')) { return editor.error("Pêşî rêzên di bin " + title + " de rake."); } editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); }); this.$tools = $tools; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root. function setLineEditing(root, editor, on) { return loadNames().then(function () { $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor || new Editor(), this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> go6vov7uj8nm9q8or3kpzapgtxhl4c6 6794052 6794047 2026-10-14T19:43:36Z Wikihez 7344 Each translation opens its template in the TemplateData dialog; + adds one in the same language; language menu on the page body 6794052 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + (before && after && !/^\s/.test(after) ? ' ' : '') + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ after each translation (its template in a dialog), + (another translation // in this language) and × (remove the line). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $add = $('<a href="#">+</a>').attr('title', 'Tercimeyeke din bi ' + (codeToName[code] || name) + ' lê zêde bike'); var $remove = $('<a href="#">×</a>').attr('title', 'Vê rêzê rake'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', code ? $add : '', ' ', $remove); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); termNodes(line).forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && (last.nextSibling.lang || /translation-adder-line-tools/.test(last.nextSibling.className)))) { last = last.nextSibling; } $(last).after($edit); $edit.toggle(self.$tools.is(':visible') || !self.$tools.parent().length); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); $add.on('click', function (e) { e.preventDefault(); templateDialog('{{W|' + code + '|}}').then(function (added) { if (!added) { return; } queueRest(function (rest) { return rest.trim() ? rest.replace(/\s*$/, '') + ', ' + added : added; }, '+' + title); }); }); $remove.on('click', function (e) { e.preventDefault(); if (line.querySelector('ul, dl')) { return editor.error("Pêşî rêzên di bin " + title + " de rake."); } editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); }); } // Shows (on) or hides the ✎, + and × on every translation line of the boxes in root. function setLineEditing(root, editor, on) { return loadNames().then(function () { $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor || new Editor(), this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 58qjpbx4oeoin5928c605xoi1bk6tr1 6794054 6794052 2026-10-14T19:45:24Z Wikihez 7344 + uses the template family already on the line 6794054 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + (before && after && !/^\s/.test(after) ? ' ' : '') + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ after each translation (its template in a dialog), + (another translation // in this language) and × (remove the line). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $add = $('<a href="#">+</a>').attr('title', 'Tercimeyeke din bi ' + (codeToName[code] || name) + ' lê zêde bike'); var $remove = $('<a href="#">×</a>').attr('title', 'Vê rêzê rake'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', code ? $add : '', ' ', $remove); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); termNodes(line).forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && (last.nextSibling.lang || /translation-adder-line-tools/.test(last.nextSibling.className)))) { last = last.nextSibling; } $(last).after($edit); $edit.toggle(self.$tools.is(':visible') || !self.$tools.parent().length); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); $add.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return notFound(); } // {{W}} of the same family as the line's first translation, so a test copy stays a test copy. var first = termCalls(found.rest)[0]; var name = first ? util.parseTemplate(first.text, 0).name.trim().replace(/^W[+-]?/, 'W') : 'W'; templateDialog('{{' + name + '|' + code + '|}}').then(function (added) { if (!added) { return; } queueRest(function (rest) { return rest.trim() ? rest.replace(/\s*$/, '') + ', ' + added : added; }, '+' + title); }); }); }); $remove.on('click', function (e) { e.preventDefault(); if (line.querySelector('ul, dl')) { return editor.error("Pêşî rêzên di bin " + title + " de rake."); } editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); }); } // Shows (on) or hides the ✎, + and × on every translation line of the boxes in root. function setLineEditing(root, editor, on) { return loadNames().then(function () { $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor || new Editor(), this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> i23xbap0gs9rz3t3t5dgmsbi0bqdfeh 6794055 6794054 2026-10-14T19:46:28Z Wikihez 7344 ✎ after a translation's transliteration; removing a middle translation keeps one comma 6794055 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ after each translation (its template in a dialog), + (another translation // in this language) and × (remove the line). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $add = $('<a href="#">+</a>').attr('title', 'Tercimeyeke din bi ' + (codeToName[code] || name) + ' lê zêde bike'); var $remove = $('<a href="#">×</a>').attr('title', 'Vê rêzê rake'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', code ? $add : '', ' ', $remove); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } $(last).after($edit); $edit.toggle(self.$tools.is(':visible') || !self.$tools.parent().length); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); $add.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return notFound(); } // {{W}} of the same family as the line's first translation, so a test copy stays a test copy. var first = termCalls(found.rest)[0]; var name = first ? util.parseTemplate(first.text, 0).name.trim().replace(/^W[+-]?/, 'W') : 'W'; templateDialog('{{' + name + '|' + code + '|}}').then(function (added) { if (!added) { return; } queueRest(function (rest) { return rest.trim() ? rest.replace(/\s*$/, '') + ', ' + added : added; }, '+' + title); }); }); }); $remove.on('click', function (e) { e.preventDefault(); if (line.querySelector('ul, dl')) { return editor.error("Pêşî rêzên di bin " + title + " de rake."); } editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); }); } // Shows (on) or hides the ✎, + and × on every translation line of the boxes in root. function setLineEditing(root, editor, on) { return loadNames().then(function () { $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor || new Editor(), this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 9v8zyp8y3l9yjbkcoi05jw8ue39u77t 6794056 6794055 2026-10-14T20:05:26Z Wikihez 7344 × per translation (the line goes with its only translation); nest existing lines under their language 6794056 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it) and // + (another translation in this language). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $add = $('<a href="#">+</a>').attr('title', 'Tercimeyeke din bi ' + (codeToName[code] || name) + ' lê zêde bike'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', code ? $add : ''); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); $add.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return notFound(); } // {{W}} of the same family as the line's first translation, so a test copy stays a test copy. var first = termCalls(found.rest)[0]; var name = first ? util.parseTemplate(first.text, 0).name.trim().replace(/^W[+-]?/, 'W') : 'W'; templateDialog('{{' + name + '|' + code + '|}}').then(function (added) { if (!added) { return; } queueRest(function (rest) { return rest.trim() ? rest.replace(/\s*$/, '') + ', ' + added : added; }, '+' + title); }); }); }); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code && codeToName[code]) { return codeToName[code]; } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code === parent; })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), codeToName[parent] || parent) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return getLangCode(li) === m.parent; })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(codeToName[m.parent] || m.parent) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; list.insertBefore(m.li, before || null); m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎, × and + on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> q7he6a2obquzig8fi5nk7osjmsn01sz 6794057 6794056 2026-10-14T20:06:38Z Wikihez 7344 Singular wording for one line to nest 6794057 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it) and // + (another translation in this language). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $add = $('<a href="#">+</a>').attr('title', 'Tercimeyeke din bi ' + (codeToName[code] || name) + ' lê zêde bike'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', code ? $add : ''); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); $add.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return notFound(); } // {{W}} of the same family as the line's first translation, so a test copy stays a test copy. var first = termCalls(found.rest)[0]; var name = first ? util.parseTemplate(first.text, 0).name.trim().replace(/^W[+-]?/, 'W') : 'W'; templateDialog('{{' + name + '|' + code + '|}}').then(function (added) { if (!added) { return; } queueRest(function (rest) { return rest.trim() ? rest.replace(/\s*$/, '') + ', ' + added : added; }, '+' + title); }); }); }); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code && codeToName[code]) { return codeToName[code]; } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code === parent; })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), codeToName[parent] || parent) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return getLangCode(li) === m.parent; })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(codeToName[m.parent] || m.parent) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; list.insertBefore(m.li, before || null); m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎, × and + on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 1q1blxk506cmlml7e1fez7y3jabyael 6794058 6794057 2026-10-14T20:07:06Z Wikihez 7344 Draw a nested line as a dd, as the saved page has it 6794058 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); return exact.concat(starts, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it) and // + (another translation in this language). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $add = $('<a href="#">+</a>').attr('title', 'Tercimeyeke din bi ' + (codeToName[code] || name) + ' lê zêde bike'); var $tools = $('<span class="translation-adder-line-tools">').append(' ', code ? $add : ''); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); $add.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found) { return notFound(); } // {{W}} of the same family as the line's first translation, so a test copy stays a test copy. var first = termCalls(found.rest)[0]; var name = first ? util.parseTemplate(first.text, 0).name.trim().replace(/^W[+-]?/, 'W') : 'W'; templateDialog('{{' + name + '|' + code + '|}}').then(function (added) { if (!added) { return; } queueRest(function (rest) { return rest.trim() ? rest.replace(/\s*$/, '') + ', ' + added : added; }, '+' + title); }); }); }); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code && codeToName[code]) { return codeToName[code]; } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code === parent; })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), codeToName[parent] || parent) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return getLangCode(li) === m.parent; })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(codeToName[m.parent] || m.parent) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎, × and + on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> nz0nw6fvnq4k9e9prp4irwsmm9ikgh7 6794064 6794058 2026-10-14T20:17:11Z Wikihez 7344 Placeholders; code prefixes in the language search, common languages first; preview stays on its line; no + on lines 6794064 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { if (li.className.indexOf('trans-') === 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code && codeToName[code]) { return codeToName[code]; } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code === parent; })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), codeToName[parent] || parent) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return getLangCode(li) === m.parent; })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(codeToName[m.parent] || m.parent) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> jy37h4zkit8xkdkxknv7di3opfi3fch 6794067 6794064 2026-10-14T20:22:30Z Wikihez 7344 Read a line's language from its trans- class when it has others too 6794067 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; return toCode(txt) || error("Ji bo \"di bin zimanê\" kod an navê zimanekî binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(codeToName[nestedCode]) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code && codeToName[code]) { return codeToName[code]; } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code === parent; })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), codeToName[parent] || parent) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return getLangCode(li) === m.parent; })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(codeToName[m.parent] || m.parent) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> f4n12syiso1fy4nr0syg4ugllnahh5f 6794072 6794067 2026-10-14T20:27:45Z Wikihez 7344 Nesting under families from Modul:malbat for en.wiktionary's groups that have no language code 6794072 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or the first span with a lang attribute. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var spans = li.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (spans[i].lang) return spans[i].lang; } return false; } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code === parent; })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return getLangCode(li) === m.parent; })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> ond0pmrbtugzozks9migbkl76wqgzdr 6794078 6794072 2026-10-14T21:04:43Z Wikihez 7344 A line's language from its own nodes only; a group written as text counts as that group 6794078 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span'); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { parent.appendChild(span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span'); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { sub.appendChild(addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { return ownNodes(line).filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit').remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); $(this).children('.translation-adder-term-edit').toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 2xp5vywux7oqgv72a6fh1ulmeye4bxu 6794088 6794078 2026-10-14T21:49:54Z Wikihez 7344 New translation goes after the last ✎ ×, and editing mode opens so it has its own 6794088 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } // Puts node at the end of a line's own content: after its last translation and that translation's ✎ ×, // before the lines nested under it. function appendToLine(line, node) { if (/(?:^|\s)translation-adder-wrap(?:\s|$)/.test(line.className) && line.firstElementChild) { line = line.firstElementChild; } var stop = null; for (var child = line.firstChild; child; child = child.nextSibling) { if (/^(UL|DL)$/.test(child.nodeName) || (child.nodeType === 1 && /(?:^|\s)translation-adder-line-tools(?:\s|$)/.test(child.className))) { stop = child; break; } } line.insertBefore(node, stop); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); // Editing mode, so the new translation has its ✎ and × at once. setLineEditing(table, editor, true); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span', { 'class': 'translation-adder-added' }); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(parent, span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span', { 'class': 'translation-adder-added' }); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(sub, addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { var nodes = []; ownNodes(line).forEach(function (n) { if (n.nodeType === 1 && /(?:^|\s)translation-adder-added(?:\s|$)/.test(n.className)) { nodes.push.apply(nodes, Array.prototype.slice.call(n.children)); } else { nodes.push(n); } }); return nodes.filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; this.line = line; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit') .add($(line).children('.translation-adder-added').children('.translation-adder-term-edit')) .remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); $(root).find('table.translations').each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $(root).find('table.translations td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); if (on) { tools.markTerms(); } $(this).find('.translation-adder-term-edit').filter(function () { return $(this).closest('li, dd')[0] === tools.line; }).toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> 512pia273zuv1us3200cjkkhub5k83m 6794090 6794088 2026-10-14T21:51:02Z Wikihez 7344 Line editing on a box given directly 6794090 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } // Puts node at the end of a line's own content: after its last translation and that translation's ✎ ×, // before the lines nested under it. function appendToLine(line, node) { if (/(?:^|\s)translation-adder-wrap(?:\s|$)/.test(line.className) && line.firstElementChild) { line = line.firstElementChild; } var stop = null; for (var child = line.firstChild; child; child = child.nextSibling) { if (/^(UL|DL)$/.test(child.nodeName) || (child.nodeType === 1 && /(?:^|\s)translation-adder-line-tools(?:\s|$)/.test(child.className))) { stop = child; break; } } line.insertBefore(node, stop); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); // Editing mode, so the new translation has its ✎ and × at once. setLineEditing(table, editor, true); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span', { 'class': 'translation-adder-added' }); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(parent, span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span', { 'class': 'translation-adder-added' }); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(sub, addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { var nodes = []; ownNodes(line).forEach(function (n) { if (n.nodeType === 1 && /(?:^|\s)translation-adder-added(?:\s|$)/.test(n.className)) { nodes.push.apply(nodes, Array.prototype.slice.call(n.children)); } else { nodes.push(n); } }); return nodes.filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; this.line = line; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit') .add($(line).children('.translation-adder-added').children('.translation-adder-term-edit')) .remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-line-tools/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); // root may be a box itself. var $tables = $(root).find('table.translations').addBack('table.translations'); $tables.each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $tables.find('td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); if (on) { tools.markTerms(); } $(this).find('.translation-adder-term-edit').filter(function () { return $(this).closest('li, dd')[0] === tools.line; }).toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> mj03o8srkh10eeugv2khl3wjnhli2b5 6794091 6794090 2026-10-14T21:51:37Z Wikihez 7344 A translation's ✎ × stop before an added translation 6794091 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } // Puts node at the end of a line's own content: after its last translation and that translation's ✎ ×, // before the lines nested under it. function appendToLine(line, node) { if (/(?:^|\s)translation-adder-wrap(?:\s|$)/.test(line.className) && line.firstElementChild) { line = line.firstElementChild; } var stop = null; for (var child = line.firstChild; child; child = child.nextSibling) { if (/^(UL|DL)$/.test(child.nodeName) || (child.nodeType === 1 && /(?:^|\s)translation-adder-line-tools(?:\s|$)/.test(child.className))) { stop = child; break; } } line.insertBefore(node, stop); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); // Editing mode, so the new translation has its ✎ and × at once. setLineEditing(table, editor, true); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span', { 'class': 'translation-adder-added' }); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(parent, span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span', { 'class': 'translation-adder-added' }); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(sub, addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { var nodes = []; ownNodes(line).forEach(function (n) { if (n.nodeType === 1 && /(?:^|\s)translation-adder-added(?:\s|$)/.test(n.className)) { nodes.push.apply(nodes, Array.prototype.slice.call(n.children)); } else { nodes.push(n); } }); return nodes.filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; this.line = line; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit') .add($(line).children('.translation-adder-added').children('.translation-adder-term-edit')) .remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-(?:line-tools|added)/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.$tools.is(':visible') || !self.$tools.parent().length); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); // root may be a box itself. var $tables = $(root).find('table.translations').addBack('table.translations'); $tables.each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $tables.find('td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.$tools.toggle(on); if (on) { tools.markTerms(); } $(this).find('.translation-adder-term-edit').filter(function () { return $(this).closest('li, dd')[0] === tools.line; }).toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> i5x6bv55klm5dn3brvnb0n4i4yfh950 6794092 6794091 2026-10-14T21:53:27Z Wikihez 7344 Editing mode kept as a flag, so a redrawn line's ✎ × show 6794092 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } // Puts node at the end of a line's own content: after its last translation and that translation's ✎ ×, // before the lines nested under it. function appendToLine(line, node) { if (/(?:^|\s)translation-adder-wrap(?:\s|$)/.test(line.className) && line.firstElementChild) { line = line.firstElementChild; } var stop = null; for (var child = line.firstChild; child; child = child.nextSibling) { if (/^(UL|DL)$/.test(child.nodeName) || (child.nodeType === 1 && /(?:^|\s)translation-adder-line-tools(?:\s|$)/.test(child.className))) { stop = child; break; } } line.insertBefore(node, stop); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); // Editing mode, so the new translation has its ✎ and × at once. setLineEditing(table, editor, true); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span', { 'class': 'translation-adder-added' }); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(parent, span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span', { 'class': 'translation-adder-added' }); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(sub, addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { var nodes = []; ownNodes(line).forEach(function (n) { if (n.nodeType === 1 && /(?:^|\s)translation-adder-added(?:\s|$)/.test(n.className)) { nodes.push.apply(nodes, Array.prototype.slice.call(n.children)); } else { nodes.push(n); } }); return nodes.filter(function (n) { return n.nodeType === 1 && n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className); }); } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; this.line = line; // Whether editing mode is on; set by setLineEditing(). this.editing = true; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit') .add($(line).children('.translation-adder-added').children('.translation-adder-term-edit')) .remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-(?:line-tools|added)/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.editing); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); // root may be a box itself. var $tables = $(root).find('table.translations').addBack('table.translations'); $tables.each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $tables.find('td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.editing = on; tools.$tools.toggle(on); if (on) { tools.markTerms(); } $(this).find('.translation-adder-term-edit').filter(function () { return $(this).closest('li, dd')[0] === tools.line; }).toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> ixh97rcyb2yu7z4ilf8flh78puwrnno 6794096 6794092 2026-10-14T22:04:06Z Wikihez 7344 ✎ × for translations marked for checking, and one pair for a traditional / simplified translation 6794096 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } // Puts node at the end of a line's own content: after its last translation and that translation's ✎ ×, // before the lines nested under it. function appendToLine(line, node) { if (/(?:^|\s)translation-adder-wrap(?:\s|$)/.test(line.className) && line.firstElementChild) { line = line.firstElementChild; } var stop = null; for (var child = line.firstChild; child; child = child.nextSibling) { if (/^(UL|DL)$/.test(child.nodeName) || (child.nodeType === 1 && /(?:^|\s)translation-adder-line-tools(?:\s|$)/.test(child.className))) { stop = child; break; } } line.insertBefore(node, stop); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); // Editing mode, so the new translation has its ✎ and × at once. setLineEditing(table, editor, true); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span', { 'class': 'translation-adder-added' }); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(parent, span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span', { 'class': 'translation-adder-added' }); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(sub, addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { var nodes = []; ownNodes(line).forEach(function (n) { if (n.nodeType === 1 && /(?:^|\s)translation-adder-added(?:\s|$)/.test(n.className)) { nodes.push.apply(nodes, Array.prototype.slice.call(n.childNodes)); } else { nodes.push(n); } }); // A translation starts with its link's span, or with the "ttbc" span of one marked for checking. // The second form after a "/" (traditional / simplified Chinese) belongs to the same translation. var terms = []; var afterSlash = false; nodes.forEach(function (n) { if (n.nodeType === 3) { if (n.data.trim()) { afterSlash = n.data.trim() === '/'; } return; } if (n.nodeType !== 1) { return; } var isTerm = /(?:^|\s)ttbc(?:\s|$)/.test(n.className) || (n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className)); if (isTerm && !(afterSlash && terms.length)) { terms.push(n); } afterSlash = false; }); return terms; } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; this.line = line; // Whether editing mode is on; set by setLineEditing(). this.editing = true; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit') .add($(line).children('.translation-adder-added').children('.translation-adder-term-edit')) .remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-(?:line-tools|added)/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.editing); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); // root may be a box itself. var $tables = $(root).find('table.translations').addBack('table.translations'); $tables.each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $tables.find('td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.editing = on; tools.$tools.toggle(on); if (on) { tools.markTerms(); } $(this).find('.translation-adder-term-edit').filter(function () { return $(this).closest('li, dd')[0] === tools.line; }).toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> srku8srkg8nzmomlj2ze68rp6o7nm30 6794097 6794096 2026-10-14T22:06:21Z Wikihez 7344 The / between traditional and simplified forms is in a span of its own 6794097 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.translation-adder.js]] * * Adds a form under each translation box ({{werger-ser}} … {{werger-bin}}) to add a line such as * * {{Z|de}}: {{W+|de|Wasser|n}} * in Kurmancî alphabetical order, and a ± link on the box heading to change its gloss. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder.js]] (by [[en:User:Conrad.Irwin]] and others). * * DefSideBoxes ([[MediaWiki:Gadget-wikt.dsb.js]]) loads it from the pencil on a Werger heading and calls * attach(root, editor) with an editor from wikt.dsb-panels, so the changes join its save queue. Without * an editor, edits pile up in this gadget's own box at the top left. * * The form reads only boxes from [[Modul:translations]] (table.translations with data-gloss); the older * div.translations boxes are left to the "translation_editor" gadget. */ /* global mw, $, AdderWrapper, LangMetadata, Editor */ require("./wikt.translation-adder/editor.js"); require("./wikt.translation-adder/language-utils.js"); require("./wikt.translation-adder/data.js"); var NAMES_PAGE = "Modul:ziman/code to canonical name.json"; var ALPHABET = "abcçdeêfghiîjklmnopqrsştuûvwxyz"; // Box templates in wikitext; the /ceribandin copies are the test versions. var TOP = /\{\{\s*[Ww]erger-ser(?:\/ceribandin)?\s*(?=[|}])/; var BOTTOM = /\{\{\s*[Ww]erger-bin(?:\/ceribandin)?\s*\}\}/; // code -> Kurmancî name, and back; filled once from NAMES_PAGE. var codeToName = null; var nameToCode = null; var namesPromise = null; function loadNames() { if (!namesPromise) { namesPromise = $.get({ url: mw.util.getUrl(NAMES_PAGE, { action: "raw", maxage: 86400, smaxage: 86400 }), dataType: "json" }).then(function (data) { codeToName = data; nameToCode = {}; Object.keys(data).forEach(function (code) { var name = data[code].toLowerCase(); if (!nameToCode[name]) { nameToCode[name] = code; } }); }); } return namesPromise; } // A language code from a code or a Kurmancî name, or null. function toCode(txt) { txt = (txt || "").trim(); if (codeToName[txt]) { return txt; } return nameToCode[txt.toLowerCase()] || null; } // The name of a language, or of a family lines are nested under. function groupName(code) { return codeToName[code] || new LangMetadata().getGroupName(code) || code; } function ucfirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // Sort key in Kurmancî alphabetical order; a letter outside the alphabet sorts after its base letter. function sortKey(s) { return Array.from(s.toLowerCase().replace(/['ʼ", -]/g, "")).map(function (ch) { var i = ALPHABET.indexOf(ch); if (i !== -1) { return String.fromCharCode(0x100 + i * 2); } i = ALPHABET.indexOf(ch.normalize("NFD").charAt(0)); return i !== -1 ? String.fromCharCode(0x101 + i * 2) : ch; }).join(""); } function compareNames(a, b) { var ka = sortKey(a), kb = sortKey(b); if (ka !== kb) { return ka < kb ? -1 : 1; } return a < b ? -1 : a > b ? 1 : 0; } function sameName(a, b) { return sortKey(a) === sortKey(b); } // The HTML of a rendered line after its "Language:" label. function afterColon(html) { var div = document.createElement('div'); div.innerHTML = html; var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(':'); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML; } var PICKER_SIZE = 12; // A name without its diacritics, so "ital" finds "îtalî". function fold(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } // folded name -> code, built on first use. var foldedNames = null; // Up to PICKER_SIZE codes for what was typed: the code itself, then names starting with it (shortest // first, so "fran" offers fransî before franc-comtoisî), then codes starting with it, common languages // first ("cm" offers cmn), then names containing it. function matchLanguages(term) { term = term.trim().toLowerCase(); var exact = codeToName[term] ? [term] : []; if (term.length < 2) { return exact; } if (!foldedNames) { foldedNames = Object.keys(nameToCode).map(function (name) { return [fold(name), nameToCode[name]]; }); } var folded = fold(term); var meta = new LangMetadata(); var starts = [], contains = []; foldedNames.forEach(function (pair) { var i = pair[0].indexOf(folded); if (pair[1] === term || i === -1) { return; } (i === 0 ? starts : contains).push(pair[1]); }); var codes = Object.keys(codeToName).filter(function (code) { return code !== term && code.indexOf(term) === 0 && starts.indexOf(code) === -1; }).sort(function (a, b) { return (meta.isCommon(b) - meta.isCommon(a)) || a.length - b.length || (a < b ? -1 : 1); }); function byName(a, b) { return compareNames(codeToName[a], codeToName[b]); } starts.sort(function (a, b) { return codeToName[a].length - codeToName[b].length || byName(a, b); }); contains = contains.filter(function (code) { return codes.indexOf(code) === -1; }); return exact.concat(starts, codes, contains.sort(byName)).slice(0, PICKER_SIZE); } // Wraps a language input in a dropdown of matching languages. A code typed in is replaced by its name // when the field is left. onChange runs whenever the value changes. function languagePicker(input, onChange) { // On the page body, so a translation box that clips its content does not cut the menu off. var menu = newNode('ul', { 'class': 'translation-adder-menu', role: 'listbox' }); var box = newNode('span', { 'class': 'translation-adder-langbox' }, input); document.body.appendChild(menu); var codes = []; var active = -1; input.setAttribute('autocomplete', 'off'); menu.style.display = 'none'; function close() { menu.style.display = 'none'; active = -1; } function choose(code) { input.value = codeToName[code]; close(); onChange(); } function render() { menu.textContent = ''; codes.forEach(function (code, i) { var item = newNode('li', { role: 'option', 'class': i === active ? 'translation-adder-menu-active' : '' }, codeToName[code], newNode('span', { 'class': 'translation-adder-menu-code' }, ' ' + code)); item.addEventListener('mousedown', function (e) { // Keeps the focus in the input, so its blur does not close the menu first. e.preventDefault(); choose(code); }); menu.appendChild(item); }); var rect = input.getBoundingClientRect(); menu.style.left = (rect.left + window.scrollX) + 'px'; menu.style.top = (rect.bottom + window.scrollY) + 'px'; menu.style.display = codes.length ? '' : 'none'; } input.addEventListener('input', function () { codes = matchLanguages(input.value); active = codes.length ? 0 : -1; render(); onChange(); }); input.addEventListener('keydown', function (e) { if (menu.style.display === 'none' || !codes.length) { return; } if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); active = (active + (e.key === 'ArrowDown' ? 1 : codes.length - 1)) % codes.length; render(); } else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); choose(codes[active]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', function () { close(); var value = input.value.trim(); if (codeToName[value]) { input.value = codeToName[value]; } onChange(); }); return box; } // Puts node at the end of a line's own content: after its last translation and that translation's ✎ ×, // before the lines nested under it. function appendToLine(line, node) { if (/(?:^|\s)translation-adder-wrap(?:\s|$)/.test(line.className) && line.firstElementChild) { line = line.firstElementChild; } var stop = null; for (var child = line.firstChild; child; child = child.nextSibling) { if (/^(UL|DL)$/.test(child.nodeName) || (child.nodeType === 1 && /(?:^|\s)translation-adder-line-tools(?:\s|$)/.test(child.className))) { stop = child; break; } } line.insertBefore(node, stop); } function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument instanceof Node) { node.appendChild(argument); } else { for (var j in argument) { if (j === 'class') { node.className = argument[j]; } else if (j == 'style') { node.style.cssText = argument[j]; } else if (typeof argument[j] == 'function') { node.addEventListener(j, argument[j], false); } else { node.setAttribute(j, argument[j]); } } } } } return node; } // Form settings kept in localStorage. function Preferences() { var KEY = "wikt-translation-adder"; var storage = mw.storage.getObject(KEY) || {}; this.get = function (name, def) { return storage[name] || def; }; this.set = function (name, value) { if (value === null || storage[name] === value) return; storage[name] = value; mw.storage.setObject(KEY, storage); }; } var util = { // Index of str (a string or RegExp) in text at or after pos, skipping comments, nowiki and templates // other than a match itself; -1 if none. getVanillaIndexOf: function (str, text, pos) { function find(s) { if (typeof s === 'string') return text.indexOf(s, pos); var i = text.substring(pos).search(s); return i === -1 ? -1 : i + pos; } pos = pos || 0; do { var cpos = text.indexOf('<!--', pos), tpos = text.indexOf('{{', pos), wpos = text.indexOf('<nowiki>', pos), spos = find(str); pos = Math.min( cpos == -1 ? Infinity : cpos, tpos == -1 ? Infinity : tpos, wpos == -1 ? Infinity : wpos, spos == -1 ? Infinity : spos ); if (pos == Infinity) return -1; if (pos == spos) return pos; var close = pos == cpos ? '-->' : pos == wpos ? '</nowiki>' : '}}'; var end = text.indexOf(close, pos); if (end == -1) return -1; pos = end + close.length; } while (true); }, // The template starting at pos: {name, end (after "}}"), params: [{key, value}]} (key null if positional). parseTemplate: function (text, pos) { var depth = 0, parts = [], start = pos + 2; for (var i = pos; i < text.length; i++) { var two = text.substr(i, 2); if (two === '{{' || two === '[[') { depth++; i++; } else if (two === '}}' || two === ']]') { depth--; i++; if (depth === 0) { parts.push(text.substring(start, i - 1)); break; } } else if (text.charAt(i) === '|' && depth === 1) { parts.push(text.substring(start, i)); start = i + 1; } } return { name: parts[0], end: i + 1, params: parts.slice(1).map(function (p) { var m = /^\s*([^=\[\]{}|]+?)\s*=([\s\S]*)$/.exec(p); return m ? { key: m[1], value: m[2] } : { key: null, value: p }; }) }; }, // The gloss (first positional parameter) of the box template starting at pos. getWikitextGloss: function (text, pos) { var params = util.parseTemplate(text, pos).params; for (var i = 0; i < params.length; i++) { if (params[i].key === null || params[i].key === '1') return params[i].value.trim(); } return ''; }, // The box template at pos with gloss as its first positional parameter. setWikitextGloss: function (text, pos, gloss) { var t = util.parseTemplate(text, pos); var done = false; var params = t.params.map(function (p) { if (!done && (p.key === null || p.key === '1')) { done = true; return p.key === null ? gloss : '1=' + gloss; } return p.key === null ? p.value : p.key + '=' + p.value; }); if (!done) params.unshift(gloss); return text.substring(0, pos) + '{{' + t.name + params.map(function (p) { return '|' + p; }).join('') + '}}' + text.substring(t.end); }, // Whether the words of the box's gloss (as the page shows it) appear, in order, in the wikitext gloss. matchGloss: function (wikitextGloss, gloss) { wikitextGloss = wikitextGloss.replace(/\[\[(?:[^\[\]|]*\|)?([^\[\]]*)\]\]/g, '$1').replace(/'{2,}/g, ''); if (/^\s*$/.test(gloss)) return /^\s*$/.test(wikitextGloss); var words = gloss.split(/[^\p{L}\p{N}]+/u).filter(Boolean); var pos = 0; for (var i = 0; i < words.length; i++) { pos = wikitextGloss.indexOf(words[i], pos); if (pos == -1) return false; } return true; }, // The table.translations of a node inside a box, or of a box heading. getTable: function (node) { var frame = $(node).closest('.NavFrame'); var table = $(node).closest('table.translations'); return table.length ? table[0] : frame.find('table.translations')[0]; }, getTransGloss: function (node) { var table = util.getTable(node); return table ? table.dataset.gloss || '' : ''; }, // [start, end] of the box's list in text: from the line after the opening template to the closing one. getTransTable: function (text, node) { var gloss = util.getTransGloss(node); var table = util.getTable(node); var tables = $('table.translations').get(); var all = [], found = []; var pos = -1; while ((pos = util.getVanillaIndexOf(TOP, text, pos + 1)) > -1) { all.push(pos); if (util.matchGloss(util.getWikitextGloss(text, pos), gloss)) found.push(pos); } if (found.length != 1) { var same = tables.filter(function (t) { return (t.dataset.gloss || '') === gloss; }); if (all.length === tables.length) { // Every box on the page is one in the wikitext: they match by order. found = [all[tables.indexOf(table)]]; } else if (found.length > 1 && same.length === found.length) { // Boxes with the same gloss are told apart by their order on the page. found = [found[same.indexOf(table)]]; } } if (found.length == 1 && found[0] !== undefined) { var start = util.parseTemplate(text, found[0]).end; start = text.indexOf('\n', start) + 1; var end = text.substring(start).search(BOTTOM); if (start > 0 && end > -1) return [start, start + end, found[0]]; } console.trace("getTransTable failure; node = %o, gloss = %o", node, gloss); return false; }, validateNoWikisyntax: function (field, nonempty) { return function (txt, error) { if (/[\{\}#]/.test(txt)) return error("Di qada " + field + " de herfên {}# bi kar neyne."); if (nonempty && !txt) return error(ucfirst(field) + " binivîse."); return txt; }; }, // Codes from [[Modul:gender and number/data]], in the order they are written. genderList: { "n": "nêr", "n-d": "nêr, dual", "n-p": "nêr, pirjimar", "m": "mê", "m-d": "mê, dual", "m-p": "mê, pirjimar", "g": "giştî", "g-d": "giştî, dual", "g-p": "giştî, pirjimar", "nt": "nêtar", "nt-d": "nêtar, dual", "nt-p": "nêtar, pirjimar", "y": "yekjimar", "d": "dual", "p": "pirjimar", "impf": "împerfektîv", "pf": "perfektîv" }, genderRows: [["n", "n-d", "n-p"], ["m", "m-d", "m-p"], ["g", "g-d", "g-p"], ["nt", "nt-d", "nt-p"], ["y", "d", "p"], ["impf", "pf"]] }; // The language name a line starts with ("Almanî: …"). function getLangName(li) { var guess = li.textContent || ''; guess = guess.substr(0, guess.indexOf(':')); return guess.replace(/^\s*/, ''); } // The language code of a line, from {{Z}}'s span.trad-xx or else its first translation's lang attribute. // Only the line's own nodes count: a group line written as text ("Sorbî:") has no code of its own. function getLangCode(li) { var own = /(?:^|\s)trans-(\S+)/.exec(li.className); if (own) return own[1]; var lang = false; for (var node = li.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (node.nodeType !== 1) { continue; } var spans = [node].concat(Array.prototype.slice.call(node.getElementsByTagName('span'))); for (var i = 0; i < spans.length; i++) { var m = /(?:^|\s)trad-(\S+)/.exec(spans[i].className); if (m) return m[1]; if (!lang && spans[i].lang) lang = spans[i].lang; } } return lang; } // Whether a line is the group line for code: {{Z|code}}, or the group's name written as text. function isGroupLine(li, code) { var own = getLangCode(li); return own ? own === code : sameName(getLangName(li), groupName(code)); } function TranslationAdders(editor, $root) { // The ± link on a box heading, which edits its gloss. function TranslationLabeller(insertDiv) { var original_span; var adder_form; var initial_value; var edit_button; var editing = false; var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('mane', true)(txt, error); } }, 'createForm': function () { var thisId = "a" + String(Math.random()).replace(".", ""); adder_form = newNode('form', { style: 'display: inline', width: 'auto', click: kill_event }, newNode('label', { 'for': thisId }, "Mane: "), newNode('input', { type: 'text', name: 'gloss', value: initial_value, style: 'width: 50%', title: 'Maneya ku ev tercime jê re ne, bi kurtî', id: thisId }), newNode('input', { type: 'submit', name: 'preview', value: 'Pêşdîtin' }) ); return adder_form; }, 'onsubmit': function (values, render) { render("''" + values.gloss + "''", function (new_html) { if (editing) toggle_editing(false); var old_html = original_span.innerHTML; editor.addEdit({ 'undo': function () { original_span.innerHTML = old_html; if (!editing) toggle_editing(); }, 'redo': function () { original_span.innerHTML = new_html; if (editing) toggle_editing(); }, 'edit': function (text) { return perform_edit(text, values.gloss); }, 'summary': 'mane: "' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '…"' : values.gloss + '"') }, original_span); }); } }; function perform_edit(wikitext, gloss) { var pos = util.getTransTable(wikitext, insertDiv); if (!pos) { editor.error("Qutiya tercimeyan nehat dîtin."); return wikitext; } return util.setWikitextGloss(wikitext, pos[2], gloss); } // Don't open and close the box when using the form. function kill_event(e) { e.stopPropagation(); } function toggle_editing() { if (editing) { adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; } editing = true; edit_button.text("Tê barkirin…"); editor.withCurrentText(function (currentText) { var pos = util.getTransTable(currentText, insertDiv); edit_button.text('±'); if (!pos) return editor.error("Qutiya tercimeyan nehat dîtin."); initial_value = util.getWikitextGloss(currentText, pos[2]); if (!original_span) { original_span = newNode('span', { 'class': 'wt-edit-recurse' }); for (var i = 0; i < insertDiv.childNodes.length; i++) { var child = insertDiv.childNodes[i]; if (child != edit_button[0] && child.className !== 'NavToggle') { original_span.appendChild(insertDiv.removeChild(child)); i--; } } insertDiv.appendChild(original_span); new AdderWrapper(editor, adderInterface, insertDiv, original_span); } original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus(); }); } edit_button = $('<a href="#" class="translation-adder-gloss">±</a>') .attr("title", "Maneya qutiyê biguherîne").css("padding", "2px").css("margin-left", "-5px") .on("click", function (e) { e.preventDefault(); kill_event(e); toggle_editing(); return false; }); $(insertDiv).prepend(edit_button); } function TranslationAdder(insertUl) { var langmetadata = new LangMetadata(); this.fields = { lang: function (txt, error) { var code = toCode(txt); if (!code) return error("Koda zimanî an navê zimanî binivîse (de, almanî, …)."); if (code == 'ku' || code == 'kmr') return error("Zimanekî ji bilî kurmancî hilbijêre."); return code; }, word: function (txt, error) { if (txt.indexOf(',') == -1 || forceComma) { forceComma = false; if (langmetadata.expectedCase(toCode(thiz.elements.lang.value), mw.config.get('wgTitle'), txt) || forceCase) { forceCase = false; return util.validateNoWikisyntax('tercime', true)(txt, error); } if (prefs.get('case-knowledge', 'none') == 'none') { return error(newNode('span', "Tercime pir caran bi herfa biçûk dest pê dikin. Heke tu bawer î ku bi herfa mezin e, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceCase = true; inputForm.onsubmit(); prefs.set('case-knowledge', 'guru'); } }, "ji bo berdewamkirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('case-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke tu ne bawer î ku ev tercime bi herfa mezin e."); error(msg); return txt; } } if (prefs.get('comma-knowledge', 'none') == 'none') { return error(newNode('span', "Her carê tenê tercimeyekê lê zêde bike. Heke ev tercimeyek e ku virgul tê de heye, ", newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { forceComma = true; inputForm.onsubmit(); prefs.set('comma-knowledge', 'guru'); } }, "ji bo lêzêdekirinê li vir bitikîne."))); } else { var msg = newNode('span', newNode('span', { style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;", click: function () { prefs.set('comma-knowledge', 'none'); try { msg.parentNode.removeChild(msg); } catch (e) { } editor.undo(); } }, "Li \"Paşve\" bitikîne"), " heke te dixwest çend tercimeyan bi hev re lê zêde bikî: ev nabe."); error(msg); return txt; } }, qual: util.validateNoWikisyntax('not'), tr: util.validateNoWikisyntax('transliterasyon'), lit: util.validateNoWikisyntax('maneya herfî'), rawPageName: util.validateNoWikisyntax('navê rûpelê'), nested: function (txt, error) { if (!txt) return ''; var group = toCode(txt) || (langmetadata.getGroupName(txt.trim()) ? txt.trim() : null); return group || error("Ji bo \"di bin zimanê\" koda zimanekî an malbatekê binivîse."); }, nclass1: util.validateNoWikisyntax('sinifa navdêrê'), nclass2: util.validateNoWikisyntax('sinifa pirjimariyê') }; var fields = this.fields; Object.keys(util.genderList).forEach(function (g) { fields[g] = 'checkbox'; }); this.createForm = function () { function createGenderNode(name, text) { var inp = document.createElement("input"); inp.type = "checkbox"; inp.name = name; var lbl = document.createElement("label"); lbl.appendChild(inp); lbl.appendChild(document.createTextNode(text)); return lbl; } var genderControls = {}; for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]); var langName = newNode('span', { 'class': 'translation-adder-langname' }); var controls = { lang: newNode('input', { size: 12, type: 'text', name: 'lang', value: prefs.get('curlang', ''), placeholder: 'ziman', title: 'Koda zimanî (de, fr, …) an navê wî (almanî, fransî, …)' }), transliteration: newNode('p', "Transliterasyon: ", newNode('input', { name: "tr", title: "Tercime bi herfên latînî." }), " (nimûne: zìmǔ ji bo 字母)"), literal: newNode('p', "Maneya herfî: ", newNode('input', { name: "lit", title: "Maneya kelîme bi kelîme ya tercimeyê." })), qualifier: newNode('p', "Not: ", newNode('input', { name: 'qual', title: "Notek kurt li ser tercimeyê" }), " (nimûne: kevn, devkî)"), display: newNode('p', "Navê rûpelê: ", newNode('input', { name: 'rawPageName', title: "Heke tercime ne forma bingehîn be, navê rûpela ku jê re lînk bê dayîn." }), " (nimûne: 疲れる ji bo 疲れた)"), nested: newNode('p', "Di bin zimanê: ", newNode('input', { name: 'nested', title: "Zimanê ku ev ziman di bin wî de tê rêzkirin" }), " (nimûne: zh ji bo mandarînî)"), genders: genderControls, nclass: newNode('p', "Sinifa navdêrê: ", newNode('input', { type: 'text', size: 4, name: 'nclass1', title: "Sinifa navdêrê di forma yekjimar de." }), " Sinifa pirjimariyê: ", newNode('input', { type: 'text', size: 4, name: 'nclass2', title: "Sinifa navdêrê di forma pirjimar de, heke hebe." })) }; var $gender = $("<p>"); util.genderRows.forEach(function (row) { var $div = $("<div>"); row.forEach(function (code) { $div.append(controls.genders[code]); }); $gender.append($div); }); controls.gender = $gender[0]; langInput = controls.lang; var showButton = newNode('span', { 'click': function () { advancedMode = !advancedMode; showButton.textContent = advancedMode ? " Kêmtir" : " Zêdetir"; updateFieldVisibility.call(langInput, true); }, 'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;" }, advancedMode ? " Kêmtir" : " Zêdetir"); function autoTransliterate() { var rawPageName = langmetadata.computeRawPageName(toCode(thiz.elements.lang.value), thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; } function updateFieldVisibility(preserve) { preserve = (preserve === true); function show() { for (var i = 0; i < arguments.length; i++) { arguments[i].style.display = arguments[i].nodeName == 'P' ? "block" : "inline"; } } function hide() { for (var i = 0; i < arguments.length; i++) arguments[i].style.display = "none"; } // Shows the remaining arguments if the first is true, else hides them. function toggle(condition) { var nodes = [].slice.call(arguments, 1); (condition ? show : hide).apply(this, nodes); } var lang = toCode(langInput.value) || ''; // A code in the field gets its name beside it, until leaving the field puts the name in. var typed = langInput.value.trim(); langName.textContent = codeToName[typed] ? " (" + codeToName[typed] + ")" : ""; if (!preserve) { autoTransliterate(); // Mandarînî goes under Çînî and so on, unless "Di bin zimanê" is changed. thiz.elements.nested.value = lang ? langmetadata.getNested(lang) : ''; } var scriptGuess = langmetadata.guessScript(lang); if (!advancedMode) { var g = lang ? langmetadata.getGenders(lang) : undefined; if (g === undefined) { hide(controls.gender); } else { for (var genderName in util.genderList) { toggle(g.indexOf(genderName) != -1, controls.genders[genderName]); } } toggle(g, controls.gender); toggle(langmetadata.hasNounClasses(lang), controls.nclass); var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang); toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration); toggle(langmetadata.needsRawPageName(lang), controls.display); hide(controls.literal, controls.qualifier, controls.nested); } else { show.apply(this, [controls.gender].concat( Object.keys(util.genderList).map(function (k) { return controls.genders[k]; }), [controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display, controls.nested])); } } var langBox = languagePicker(langInput, function () { updateFieldVisibility.call(langInput); }); window.setTimeout(function () { updateFieldVisibility.call(langInput); }, 0); inputForm = newNode('form', newNode('p', "Tercimeyekê lê zêde bike: ", langBox, langName, newNode('b', ': '), newNode('input', { 'name': 'word', placeholder: 'tercime', title: 'Tercime bi wî zimanî', size: 20, keyup: autoTransliterate, change: autoTransliterate }), newNode('input', { 'type': 'submit', 'value': 'Pêşdîtin' }), showButton ), controls.gender, controls.nclass, controls.transliteration, controls.literal, controls.display, controls.qualifier, controls.nested ); return inputForm; }; this.onsubmit = function (values, render) { var wikitext; langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) { if (needsRawPageName) { values.rawPageName = rawPageName; values.wordMinus = ''; } else { values.rawPageName = ''; values.wordMinus = rawPageName; } langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) { var templateBody = '|' + values.lang + '|' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + (values.nclass1 ? '|c' + values.nclass1 : '') + (values.nclass2 ? '|c' + values.nclass2 : ''); templateBody += Object.keys(util.genderList).map(function (k) { return values[k] ? '|' + k : ''; }).join(""); templateBody += (values.tr ? '|tr=' + values.tr : '') + (values.lit ? '|lit=' + values.lit : '') + (values.rawPageName ? '|cuda=' + values.word : ''); var qualifier = values.qual ? ' {{i|' + values.qual + '}}' : ''; wikitext = '{{Z|' + values.lang + '}}: {{' + (hasInterwiki ? "W+" : "W") + templateBody + "}}" + qualifier; render(wikitext, function (html) { registerEdits(values, wikitext, html); }); }); }); }; var thiz = this; var prefs = new Preferences(); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false; function resetElements() { prefs.set('more-display', advancedMode ? 'block' : 'none'); ['word', 'nclass1', 'nclass2', 'lit', 'tr', 'rawPageName', 'qual'].forEach(function (f) { thiz.elements[f].value = ''; }); Object.keys(util.genderList).forEach(function (g) { thiz.elements[g].checked = false; }); prefs.set('curlang', thiz.elements.lang.value); } // Runs after the new line has been rendered: finds where it goes and registers the edit. function registerEdits(values, wikitext, content) { var li = newNode('li', { 'class': 'trans-' + values.lang }); li.innerHTML = content; var lang = getLangName(li); var summary = '+' + codeToName[values.lang] + ': [[' + langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) + ']]'; var insertBefore = null; var nextLanguage = null; var nestedCode = values.nested; // A top-level line for the language already there takes the translation, and then moves under its group. var table = util.getTable(insertUl); var nestAfter = nestedCode && misplacedLines(table, values.lang).length ? values.lang : null; if (nestAfter) { nestedCode = ''; } var nestedHeading = nestedCode ? ucfirst(groupName(nestedCode)) : ''; var nestedWikitext = "\n* {{Z|" + nestedCode + "}}:\n*: " + wikitext; function addEdit(edit, span) { var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"]; editor.addEdit({ 'undo': function () { edit.undo(); if (fields.slice(1).every(function (f) { return thiz.elements[f].value === ""; })) { for (var i = 0; i < fields.length; i++) { thiz.elements[fields[i]].value = values[fields[i]]; } for (var genderName in util.genderList) { thiz.elements[genderName].checked = values[genderName]; } } }, 'redo': function () { edit.redo(); // Editing mode, so the new translation has its ✎ and × at once. setLineEditing(table, editor, true); for (var i = 0; i < fields.length; i++) { if (thiz.elements[fields[i]].value != values[fields[i]]) return; } resetElements(); }, 'edit': edit.edit, 'summary': summary }, span); if (nestAfter) { editor.addEdit(nestEdit(table, nestAfter), null); } } if (lang && !nestedCode) { // Every line in this box. var lis = $(insertUl).parent().parent().find('li').get() .concat($(insertUl).parent().parent().find('dd').get()); for (var j = 0; j < lis.length; j++) { if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln && sameName(ln, lang)) { var span = newNode('span', { 'class': 'translation-adder-added' }); var parent = lis[j]; if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) { span.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(parent, span); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to the language's existing line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { var node = parent.firstChild; var hastrans = false; while (node) { if (node.nodeType == 1) { var nn = node.nodeName; if (nn == 'UL' || nn == 'DL') { span.innerHTML = (hastrans ? ", " : " ") + afterColon(content); addEdit({ 'redo': function () { parent.insertBefore(span, node); }, 'undo': function () { parent.removeChild(span); }, 'edit': getEditFunction(values, wikitext, ln, values.lang, function (text, ipos) { // Adding to a language that has nested lines under it. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + (hastrans ? "," : "") + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, span); return resetElements(); } else { hastrans = true; } } node = node.nextSibling; } } } else if (ln && compareNames(ln, lang) > 0 && (!nextLanguage || compareNames(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') { nextLanguage = ln; var before = lis[j]; insertBefore = [{ 'redo': function () { before.parentNode.insertBefore(li, before); }, 'undo': function () { before.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, ln, getLangCode(before), function (text, ipos) { // A new line before the next language's line. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend); }) }, li]; } } } if (nestedCode) { nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>"; var tops = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var k = 0; k < tops.length; k++) { if (tops[k].getElementsByTagName('form').length > 0) continue; // Only top-level lines. if (tops[k].parentNode.parentNode.nodeName != 'TD') continue; var tln = getLangName(tops[k]); if (tln && sameName(tln, nestedHeading)) { var group = tops[k]; var sublis = group.getElementsByTagName('li'); if (!sublis.length) sublis = group.getElementsByTagName('dd'); if (sublis.length == 0) { var dd = newNode('dd', { 'class': 'trans-' + values.lang }); var dl = newNode('dl', dd); dd.innerHTML = content; addEdit({ 'redo': function () { group.appendChild(dl); }, 'undo': function () { group.removeChild(dl); }, 'edit': getEditFunction(values, wikitext, nestedHeading, nestedCode, function (text, ipos) { // The first nested line under the group's line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); }) }, dd); return resetElements(); } var item = newNode(sublis[0].nodeName, { 'class': 'trans-' + values.lang }); var linestart = item.nodeName == 'DD' ? '\n*: ' : '\n** '; item.innerHTML = content; for (var m = 0; m < sublis.length; m++) { var subln = getLangName(sublis[m]); var sub = sublis[m]; if (subln && sameName(subln, lang)) { var addSpan = newNode('span', { 'class': 'translation-adder-added' }); addSpan.innerHTML = ", " + afterColon(content); addEdit({ 'redo': function () { appendToLine(sub, addSpan); }, 'undo': function () { sub.removeChild(addSpan); }, 'edit': getEditFunction(values, wikitext, lang, values.lang, function (text, ipos) { // Adding to an existing nested line. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "," + wikitext.substr(wikitext.indexOf(':') + 1) + text.substr(lineend); }) }, addSpan); return resetElements(); } else if (subln && langmetadata.nestsBefore(lang, subln)) { addEdit({ 'redo': function () { sub.parentNode.insertBefore(item, sub); }, 'undo': function () { sub.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, subln, getLangCode(sub), function (text, ipos) { // A nested line in order. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } } var last = sublis[sublis.length - 1]; addEdit({ 'redo': function () { last.parentNode.appendChild(item); }, 'undo': function () { last.parentNode.removeChild(item); }, 'edit': getEditFunction(values, wikitext, getLangName(last), getLangCode(last), function (text, ipos) { // A nested line at the end of its group. var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); }) }, item); return resetElements(); } else if (tln && compareNames(tln, nestedHeading) > 0 && (!nextLanguage || compareNames(tln, nextLanguage) < 0)) { nextLanguage = tln; var next = tops[k]; insertBefore = [{ 'redo': function () { next.parentNode.insertBefore(li, next); }, 'undo': function () { next.parentNode.removeChild(li); }, 'edit': getEditFunction(values, wikitext, tln, getLangCode(next), function (text, ipos) { // A new group line with the nested line under it. var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + nestedWikitext + text.substr(lineend); }) }, li]; } } wikitext = "{{Z|" + nestedCode + "}}:\n*: " + wikitext; } li.className = "trans-" + values.lang; if (insertBefore) { addEdit(insertBefore[0], insertBefore[1]); } else { // After the last line. addEdit({ 'redo': function () { insertUl.appendChild(li); }, 'undo': function () { insertUl.removeChild(li); }, 'edit': getEditFunction(values, wikitext) }, li); } return resetElements(); } // The wikitext change for this submission. With findLanguage, callback gets the position of that // language's line; without, the line goes at the end of the box. function getEditFunction(values, wikitext, findLanguage, findLangCode, callback) { return function (text) { var p = util.getTransTable(text, insertUl); if (!p) return editor.error("Qutiya tercimeyan ji bo '" + values.lang + ":" + values.word + "' nehat dîtin. Maneyên qutiyan divê ji hev cuda bin."); var stapos = p[0]; var endpos = p[1]; if (findLanguage) { var box = text.substring(stapos, endpos); var sub = values.nested ? "[:*]?" : ""; var escapedName = mw.util.escapeRegExp(findLanguage); var patterns = [ "\\*" + sub + " ?\\[\\[" + escapedName + "\\]\\]:", "\\*" + sub + " ?" + escapedName + ":" ]; if (findLangCode) patterns.unshift("\\*" + sub + " ?\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(findLangCode) + "\\s*(?:\\|[^{}]*)?\\}\\}\\s*:"); var ipos = -1; for (var i = 0; i < patterns.length && ipos == -1; i++) { ipos = box.search(new RegExp(patterns[i], "i")); } if (ipos > -1) { return callback(text, ipos + stapos); } return editor.error("Rêza zimanê " + findLanguage + " nehat dîtin. Ji kerema xwe rêzê rast bike."); } return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); }; } } function createTranslationAdderRow(li) { var $ul = $("<ul>").append(li); return $("<tr>") .addClass("translation-adder-row") .append($("<td>").attr("colspan", "3").css("text-align", "left").append($ul)); } $root.find('table.translations').each(function () { if (this.dataset.translationAdder) { return; } this.dataset.translationAdder = "1"; var td = $(this).find("td.translations-cell")[0]; if (!td) { return; } var uls = $(td).children("ul").get(); if (uls.length == 0) { uls = [td.appendChild(newNode('ul'))]; } var li = document.createElement('li'); $(this).append(createTranslationAdderRow(li)); new AdderWrapper(editor, new TranslationAdder(uls[uls.length - 1]), li); var head = $(this).closest('.NavFrame').children('.NavHead')[0]; if (head) { new TranslationLabeller(head); } }); } // HTML for wikitext as this page renders it, without the paragraph around it. function render(wikitext) { return new mw.Api().parse(wikitext, { title: mw.config.get('wgPageName'), pst: true, disablelimitreport: true }).then(function (html) { var $out = $('<div>').html(html); var $p = $out.find('p').first(); return ($p.length ? $p : $out.children().first()).html().trim(); }); } // The wikitext of a translation line: {start, restStart, end, rest}, where rest is what follows // "Language:"; null when the line is not found. function findLine(text, line) { var box = util.getTransTable(text, line); if (!box) { return null; } var code = getLangCode(line); var name = getLangName(line); var label = code ? "\\{\\{\\s*Z\\s*\\|\\s*" + mw.util.escapeRegExp(code) + "\\s*(?:\\|[^{}\\n]*)?\\}\\}" : "(?:\\[\\[" + mw.util.escapeRegExp(name) + "\\]\\]|" + mw.util.escapeRegExp(name) + ")"; var part = text.substring(box[0], box[1]); var m = new RegExp("^\\*[:*]* ?" + label + "\\s*:[ \\t]*(.*)$", "im").exec(part); if (!m) { return null; } var start = box[0] + m.index; var end = start + m[0].length; return { start: start, restStart: end - m[1].length, end: end, rest: m[1], label: text.substring(start, end - m[1].length) }; } // A line's own nodes: everything before the lines nested under it, without this gadget's buttons. function ownNodes(line) { var nodes = []; for (var node = line.firstChild; node && !/^(UL|DL)$/.test(node.nodeName); node = node.nextSibling) { if (!(node.nodeType === 1 && /(?:^|\s)translation-adder-(?:line-tools|term-edit)(?:\s|$)/.test(node.className))) { nodes.push(node); } } return nodes; } var TERM_CALL = /\{\{\s*W[+-]?(?:\/ceribandin)?\s*\|/g; // The {{W}} calls in the wikitext after "Language:", in order: [{start, end, text}]. function termCalls(rest) { var out = []; var m; TERM_CALL.lastIndex = 0; while ((m = TERM_CALL.exec(rest))) { var end = util.parseTemplate(rest, m.index).end; out.push({ start: m.index, end: end, text: rest.substring(m.index, end) }); TERM_CALL.lastIndex = end; } return out; } // rest with its i-th {{W}} call replaced by text, or removed with the comma that joins it to the others. function replaceTerm(rest, i, text) { var call = termCalls(rest)[i]; if (!call) { return null; } if (text) { return rest.substring(0, call.start) + text + rest.substring(call.end); } var before = rest.substring(0, call.start); var after = rest.substring(call.end); if (/,\s*$/.test(before)) { before = before.replace(/\s*,\s*$/, ''); } else { after = after.replace(/^\s*,\s*/, ''); } return (before + after).trim(); } // The rendered translations of a line, in order: the element each {{W}} call starts with. function termNodes(line) { var nodes = []; ownNodes(line).forEach(function (n) { if (n.nodeType === 1 && /(?:^|\s)translation-adder-added(?:\s|$)/.test(n.className)) { nodes.push.apply(nodes, Array.prototype.slice.call(n.childNodes)); } else { nodes.push(n); } }); // A translation starts with its link's span, or with the "ttbc" span of one marked for checking. // The second form after a "/" (traditional / simplified Chinese) belongs to the same translation. var terms = []; var afterSlash = false; nodes.forEach(function (n) { if (n.nodeType === 3) { if (n.data.trim()) { afterSlash = n.data.trim() === '/'; } return; } if (n.nodeType !== 1) { return; } // The separator is drawn in a span of its own. if (!n.lang && n.textContent.trim() === '/') { afterSlash = true; return; } var isTerm = /(?:^|\s)ttbc(?:\s|$)/.test(n.className) || (n.lang && !/(?:^|\s)(?:tr|ts)(?:\s|$)/.test(n.className)); if (isTerm && !(afterSlash && terms.length)) { terms.push(n); } afterSlash = false; }); return terms; } // The template dialog of [[MediaWiki:Gadget-wikt.cite.js]], built from the template's TemplateData. // Resolves with the edited call, "" when it is removed, or null when cancelled. function templateDialog(call) { return mw.loader.using('ext.gadget.wikt.cite').then(function (require) { return require('ext.gadget.wikt.cite').openTemplateDialog(call); }); } // On one translation line: ✎ and × after each translation (its template in a dialog, and removing it). function LineTools(editor, line) { var name = getLangName(line); if (!name) { return; } var code = getLangCode(line); var title = ucfirst(codeToName[code] || name); var $tools = $('<span class="translation-adder-line-tools">'); var self = this; this.$tools = $tools; this.line = line; // Whether editing mode is on; set by setLineEditing(). this.editing = true; var nested = line.querySelector('ul, dl'); if (nested) { $(nested).before($tools); } else { $(line).append($tools); } function notFound() { return editor.error("Rêza " + title + " di wîkîmetnê de nehat dîtin."); } // Queues a new text after "Language:", drawn in place from its rendering. function queueRest(makeRest, summary) { editor.withCurrentText(function (text) { var found = findLine(text, line); var rest = found && makeRest(found.rest); if (rest === null || rest === undefined) { return notFound(); } render(found.label.replace(/^\*[:*]* ?/, '') + ' ' + rest).then(function (html) { var before = ownNodes(line); var holder = document.createElement('span'); holder.innerHTML = html; var after = Array.prototype.slice.call(holder.childNodes); editor.addEdit({ redo: function () { before.forEach(function (n) { $(n).detach(); }); after.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, undo: function () { after.forEach(function (n) { $(n).detach(); }); before.forEach(function (n) { line.insertBefore(n, $tools[0]); }); self.markTerms(); }, edit: function (current) { var at = findLine(current, line); var next = at && makeRest(at.rest); if (next === null || next === undefined) { return notFound(); } return current.substring(0, at.restStart) + next + current.substring(at.end); }, summary: summary }, line); }); }); } // The ✎ after each translation, put back whenever the line is redrawn. this.markTerms = function () { $(line).children('.translation-adder-term-edit') .add($(line).children('.translation-adder-added').children('.translation-adder-term-edit')) .remove(); var terms = termNodes(line); terms.forEach(function (term, i) { var $edit = $('<a href="#" class="translation-adder-term-edit">✎</a>').attr('title', 'Vê tercimeyê biguherîne'); // After everything the translation draws: its interwiki link, gender and transliteration. var last = term; while (last.nextSibling && !/^(UL|DL)$/.test(last.nextSibling.nodeName) && terms.indexOf(last.nextSibling) === -1 && !(last.nextSibling.nodeType === 3 && /[,;]/.test(last.nextSibling.data)) && !(last.nextSibling.nodeType === 1 && /translation-adder-(?:line-tools|added)/.test(last.nextSibling.className))) { last = last.nextSibling; } var $del = $('<a href="#" class="translation-adder-term-edit">×</a>').attr('title', 'Vê tercimeyê rake'); $(last).after($edit, $del); $edit.add($del).toggle(self.editing); // The only translation of a line with nothing under it takes the line with it. $del.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); if (!found || !termCalls(found.rest)[i]) { return notFound(); } if (termCalls(found.rest).length === 1 && !line.querySelector('ul, dl')) { removeLine(); } else { queueRest(function (rest) { return replaceTerm(rest, i, ''); }, '−' + title); } }); }); $edit.on('click', function (e) { e.preventDefault(); editor.withCurrentText(function (text) { var found = findLine(text, line); var call = found && termCalls(found.rest)[i]; if (!call) { return notFound(); } templateDialog(call.text).then(function (edited) { if (edited === null || edited === call.text) { return; } queueRest(function (rest) { return replaceTerm(rest, i, edited); }, (edited ? '±' : '−') + title); }); }); }); }); }; this.markTerms(); function removeLine() { editor.addEdit({ redo: function () { line.classList.add('translation-adder-removed'); }, undo: function () { line.classList.remove('translation-adder-removed'); }, edit: function (current) { var at = findLine(current, line); if (!at) { return notFound(); } return current.substring(0, at.start) + current.substring(at.end + (current.charAt(at.end) === '\n' ? 1 : 0)); }, summary: '−' + title }, line); } } // ---- nesting: a language en.wiktionary nests (cmn) goes under its group's line ({{Z|zh}}) ---- // The language code of a wikitext line, from its {{Z}}. function lineCode(l) { var m = /^\*[:*]* ?\{\{\s*Z\s*\|\s*([^|}\s]+)/.exec(l); return m ? m[1] : null; } // The language name of a wikitext line, for sorting. function lineName(l) { var code = lineCode(l); if (code) { return groupName(code); } var m = /^\*[:*]* ?(?:\[\[)?([^\]:|]+)/.exec(l); return m ? m[1].trim() : ''; } function isChildLine(l) { return /^\*[:*]/.test(l) && !/^\*[:*][:*]/.test(l); } // text with the top-level lines of node's box whose language nests (only that language, if given) moved // under their group line, which is added in order where there is none. A group that already has a line for // the language gets its translations added to it. function nestLines(text, node, only) { var p = util.getTransTable(text, node); if (!p) { return null; } var meta = new LangMetadata(); var body = text.substring(p[0], p[1]); var trimmed = body.replace(/\n+$/, ''); var tail = body.slice(trimmed.length); var head = [], entries = []; trimmed.split('\n').forEach(function (l) { if (/^\*(?![:*])/.test(l)) { entries.push({ line: l, code: lineCode(l), children: [] }); } else if (entries.length) { entries[entries.length - 1].children.push(l); } else { head.push(l); } }); var moved = 0; entries.slice().forEach(function (e) { var parent = e.code && meta.getNested(e.code); if (!parent || (only && e.code !== only)) { return; } entries.splice(entries.indexOf(e), 1); var group = entries.filter(function (x) { return x.code ? x.code === parent : sameName(lineName(x.line), groupName(parent)); })[0]; if (!group) { group = { line: '* {{Z|' + parent + '}}:', code: parent, children: [] }; var at = entries.findIndex(function (x) { return compareNames(lineName(x.line), groupName(parent)) > 0; }); entries.splice(at === -1 ? entries.length : at, 0, group); } var prefix = group.children.some(function (c) { return /^\*\*/.test(c); }) ? '**' : '*:'; var deeper = e.children.map(function (c) { return prefix + c.slice(1); }); var same = group.children.findIndex(function (c) { return isChildLine(c) && lineCode(c) === e.code; }); if (same !== -1) { var terms = e.line.replace(/^\*\s*\{\{[^{}]*\}\}\s*:\s*/, ''); group.children[same] = group.children[same].replace(/\s*$/, '') + (terms ? ', ' + terms : ''); [].splice.apply(group.children, [same + 1, 0].concat(deeper)); } else { var name = codeToName[e.code] || e.code; var at2 = group.children.findIndex(function (c) { return isChildLine(c) && compareNames(lineName(c), name) > 0; }); [].splice.apply(group.children, [at2 === -1 ? group.children.length : at2, 0] .concat([prefix + ' ' + e.line.replace(/^\*\s*/, '')], deeper)); } moved++; }); if (!moved) { return text; } var out = head.slice(); entries.forEach(function (x) { out.push(x.line); out.push.apply(out, x.children); }); return text.substring(0, p[0]) + out.join('\n') + tail + text.substring(p[1]); } // The top-level lines of a box. function topLines(table) { return $(table).find('td.translations-cell').first().children('ul').children('li').get(); } // The top-level lines whose language nests: [{li, code, parent}]. function misplacedLines(table, only) { var meta = new LangMetadata(); return topLines(table).map(function (li) { var code = getLangCode(li); var parent = code && meta.getNested(code); return parent && (!only || code === only) ? { li: li, code: code, parent: parent } : null; }).filter(Boolean); } // Draws nestLines() on the page: the lines move under their group, highlighted. Returns the undo. function nestDom(table, only) { var undos = []; misplacedLines(table, only).forEach(function (m) { var group = topLines(table).filter(function (li) { return isGroupLine(li, m.parent); })[0]; if (!group) { var made = newNode('li', { 'class': 'trans-' + m.parent + ' translation-adder-new' }, ucfirst(groupName(m.parent)) + ':'); var next = topLines(table).filter(function (li) { return compareNames(getLangName(li), getLangName(made)) > 0; })[0]; m.li.parentNode.insertBefore(made, next || null); group = made; undos.push(function () { $(made).remove(); }); } var list = $(group).children('dl, ul')[0]; if (!list) { var newList = list = group.appendChild(newNode('dl')); undos.push(function () { $(newList).remove(); }); } var home = m.li.parentNode, after = m.li.nextSibling; var item = $(list).children().filter(function () { return getLangCode(this) === m.code; })[0]; if (item) { var span = newNode('span', { 'class': 'translation-adder-new' }); var holder = document.createElement('div'); ownNodes(m.li).forEach(function (n) { holder.appendChild(n.cloneNode(true)); }); span.innerHTML = ', ' + afterColon(holder.innerHTML); var sub = $(item).children('dl, ul')[0]; item.insertBefore(span, sub || null); m.li.style.display = 'none'; undos.push(function () { $(span).remove(); m.li.style.display = ''; }); } else { var before = $(list).children().filter(function () { return compareNames(getLangName(this), getLangName(m.li)) > 0; })[0]; // In a "*:" list the line is drawn as a <dd>, as the saved page will have it. var wrap = list.nodeName === 'DL' ? newNode('dd', { 'class': 'translation-adder-wrap' }) : null; if (wrap) { wrap.appendChild(m.li); list.insertBefore(wrap, before || null); } else { list.insertBefore(m.li, before || null); } m.li.classList.add('translation-adder-new'); undos.push(function () { m.li.classList.remove('translation-adder-new'); home.insertBefore(m.li, after); $(wrap).remove(); }); } }); return function () { undos.reverse().forEach(function (f) { f(); }); }; } // The change that nests the lines of table's box (only one language's, if given). function nestEdit(table, only) { var undo = null; return { redo: function () { undo = nestDom(table, only); }, undo: function () { if (undo) { undo(); undo = null; } }, edit: function (text) { return nestLines(text, table, only); }, summary: 'tercime di bin zimanên xwe de' }; } // Shows (on) or hides the ✎ and × on every translation line of the boxes in root, and on a box with // lines that belong under another language a link that moves them there. function setLineEditing(root, editor, on) { return loadNames().then(function () { editor = editor || new Editor(); // root may be a box itself. var $tables = $(root).find('table.translations').addBack('table.translations'); $tables.each(function () { var table = this; var $row = $(table).data('translationAdderNest'); if (!$row && on) { var count = misplacedLines(table).length; if (!count) { return; } var $nest = $('<a href="#">').text(count === 1 ? 'Rêzekê di bin zimanê wê de bi cih bike' : count + ' rêzan di bin zimanên wan de bi cih bike'); $row = $('<tr class="translation-adder-nest-row">').append($('<td colspan="3">').append('↳ ', $nest)); $(table).find('td.translations-cell').first().parent().before($row); $(table).data('translationAdderNest', $row); $nest.on('click', function (e) { e.preventDefault(); editor.addEdit(nestEdit(table), null); $row.remove(); }); } if ($row) { $row.toggle(on); } }); $tables.find('td.translations-cell').find('li, dd').each(function () { var tools = $(this).data('translationAdderTools'); if (!tools && on) { tools = new LineTools(editor, this); $(this).data('translationAdderTools', tools); } if (tools && tools.$tools) { tools.editing = on; tools.$tools.toggle(on); if (on) { tools.markTerms(); } $(this).find('.translation-adder-term-edit').filter(function () { return $(this).closest('li, dd')[0] === tools.line; }).toggle(on); } }); }); } // Adds the forms to the boxes in root (an element or jQuery object). editor collects the changes: by // default this gadget's own Editor; DefSideBoxes passes one that queues them in its save box. function attach(root, editor) { return loadNames().then(function () { TranslationAdders(editor || new Editor(), $(root)); }); } window.wikt = window.wikt || {}; window.wikt.translationAdder = { attach: attach, setLineEditing: setLineEditing, compareNames: compareNames }; module.exports = window.wikt.translationAdder; // </nowiki> kz2vaasl3uvvnqrkqcxvzw8pkblwf5i Bikarhêner:Wikihez/Gadget-test/translation-adder/editor.js 2 1359438 6793966 2026-10-14T18:20:21Z Wikihez 7344 Test copy of the translation adder gadget (fork of en.wiktionary's TranslationAdder) 6793966 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: the page editor (edit stack, undo/redo, * save box) and AdderWrapper, which runs an adder's form. Forked from en.wiktionary's * [[en:MediaWiki:Gadget-Editor.js]] (by [[en:User:Conrad.Irwin]]). */ /* global mw, $ */ window.PageEditor = function(title) { this.CheckOutForEdit = function() { return new mw.Api().get({ action: 'query', prop: 'revisions', rvprop: ['ids', 'content', 'timestamp'], titles: String(title), formatversion: '2', curtimestamp: true }) .then(function(data) { var page, revision; if (!data.query || !data.query.pages) { return $.Deferred().reject('unknown'); } page = data.query.pages[0]; if (!page || page.missing) { return $.Deferred().reject('nocreate-missing'); } revision = page.revisions[0]; this.baserevid = revision.revid; this.basetimestamp = revision.timestamp; this.curtimestamp = data.curtimestamp; return revision.content; }); } this.Save = function(newWikitext, params) { var editParams = typeof params === 'object' ? params : { text: String(params) }; return new mw.Api().postWithEditToken($.extend({ action: 'edit', title: title, formatversion: '2', text: newWikitext, // Protect against errors and conflicts assert: mw.user.isAnon() ? undefined : 'user', baserevid: this.baserevid, basetimestamp: this.basetimestamp, starttimestamp: this.curtimestamp, nocreate: true }, editParams)); } } /** * A generic page editor for the current page. * * This is a singleton and it displays a small interface in the top left after * the first edit has been registered. * * @public * this.page * this.addEdit * this.error * */ window.Editor = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; this.page = new PageEditor(mw.config.get('wgPageName')); // get the current text of the article and call the callback with it // NOTE: This function also acts as a loose non-re-entrant lock to protect currentText. this.withCurrentText = function(callback) { if (callbacks.length == 0) { callbacks = [callback]; for (var i = 0; i < callbacks.length; i++) { callbacks[i](currentText); } return callbacks = []; } if (callbacks.length > 0) { return callbacks.push(callback); } callbacks = [callback]; thiz.page.CheckOutForEdit().then(function(wikitext) { if (wikitext === null) return thiz.error("Têkilî bi serverê re çênebû."); currentText = originalText = wikitext; for (var i = 0; i < callbacks.length; i++) { callbacks[i](currentText); } callbacks = []; }); } // A decorator for withCurrentText function performSequentially(f) { return (function() { var the_arguments = arguments; thiz.withCurrentText(function() { f.apply(thiz, the_arguments); }); }); } // add an edit to the editstack function addEdit(edit, node, fromRedo) { withPresenceShowing(false, function() { if (node) { nodestack.push(node); node.style.cssText = "border: 2px var(--wikt-palette-green-4, #00FF00) dashed;" } if (!fromRedo) redostack = []; var ntext = false; try { ntext = edit.edit(currentText); if (ntext && ntext != currentText) { edit.redo(); currentText = ntext; } else return false; } catch (e) { // TODO Uncaught TypeError: Object [object Window] has no method 'error' // I may have just fixed this by changing "this" below to "thiz" ... thiz.error("Xeta: " + e); } editstack.push(edit); }); } this.addEdit = performSequentially(addEdit); // display an error to the user this.error = function(message) { console.trace(message); if (!errorlog) { errorlog = $('<ul>').css("margin", "0px -10px -10px -10px") .css("padding", "10px")[0]; // jQuery $.css destroys var(...) errorlog.style.backgroundColor = "var(--wikt-palette-pink, #FFDDDD)"; withPresenceShowing(true, function(presence) { presence.appendChild(errorlog); }); } errorlog.appendChild($('<li>').text(message)[0]); } var thiz = this; // this is set incorrectly when private functions are used as callbacks. var editstack = []; // A list of the edits that have been applied to get currentText var redostack = []; // A list of the edits that have been recently undone. var nodestack = []; // A lst of nodes to which we have added highlighting var callbacks = {}; // A list of onload callbacks (initially .length == undefined) var originalText = ""; // What was the contents of the page before we fiddled? var currentText = ""; // What is the contents now? var errorlog; // The ul for sticking errors in. var $savelog; // The ul for save messages. //Move an edit from the editstack to the redostack function undo() { if (editstack.length == 0) return false; var edit = editstack.pop(); redostack.push(edit); edit.undo(); var text = originalText; for (var i = 0; i < editstack.length; i++) { var ntext = false; try { ntext = editstack[i].edit(text); } catch (e) { thiz.error("Xeta: " + e); } if (ntext && ntext != text) { text = ntext; } else { editstack[i].undo(); editstack = editstack.splice(0, i); break; } } currentText = text; return true; } this.undo = performSequentially(undo); //Move an edit from the redostack to the editstack function redo() { if (redostack.length == 0) return; var edit = redostack.pop(); addEdit(edit, null, true); } this.redo = performSequentially(redo); function withPresenceShowing(broken, callback) { if (arguments.callee.presence) { if (broken || arguments.callee.presence.querySelector("button")) { arguments.callee.presence.style.display = "block"; return callback(arguments.callee.presence); } else { try { arguments.callee.presence.parentNode.removeChild(arguments.callee.presence); } catch (e) { } } } var presence = $('<div>').css("position", "fixed") .css("top", "0px") .css("left", "0px") .css("z-index", "10") .css("padding", "30px")[0]; // jQuery $.css destroys var(...) presence.style.backgroundColor = "var(--wikt-palette-green-4, #00FF00)"; window.setTimeout(function() { presence.style.backgroundColor = "var(--wikt-palette-dulllightblue, #CCCCFF)"; presence.style.padding = "10px"; }, 400); var closeButton = $('<div>').css("position", "relative") .css("top", "0px") .css("left", "0px") .css("margin", "-10px") .css("cursor", "pointer") .on("click", performSequentially(close)) .text("X")[0]; // jQuery $.css destroys var(...) closeButton.style.color = "var(--wikt-palette-blue, #0000FF)"; presence.appendChild(closeButton); document.body.insertBefore(presence, document.body.firstChild); var contents = $('<p>').css('text-align', 'center') .append($('<b>').text('Guhartina rûpelê')).append('<br>'); if (!broken) { contents.append($('<button>').text("Guhartinan qeyd bike") .attr('title', 'Guhartinên xwe qeyd bike [s]') .attr('accesskey', 's') .on("click", save)); contents.append($('<br>')); contents.append($('<button>').text("Paşve") .attr('title', 'Guhartina dawî paşve bibe [z]') .attr('accesskey', 'z') .on("click", thiz.undo)); contents.append($('<button>').text("Dîsa bike").on('click', thiz.redo)); mw.loader.using('mediawiki.util').then(function() { contents.children().updateTooltipAccessKeys(); }); } presence.appendChild(contents[0]); arguments.callee.presence = presence; callback(presence); } // Remove the button function close() { while (undo()) ; withPresenceShowing(true, function(presence) { presence.style.display = "none"; if (errorlog) { errorlog.parentNode.removeChild(errorlog); errorlog = false; } }); } //Send the currentText back to the server to save. function save() { thiz.withCurrentText(function() { if (editstack.length == 0) return; var cleanup_callbacks = callbacks; callbacks = []; var sum = {}; var tags = []; for (var i = 0; i < editstack.length; i++) { sum[editstack[i].summary] = true; if (editstack[i].after_save) cleanup_callbacks.push(editstack[i].after_save); if (editstack[i].tags) tags = tags.concat(editstack[i].tags.filter(tag => !!tag)); } var summary = ""; for (var name in sum) { summary += name + " "; } editstack = []; redostack = []; var saveLi = $('<li>').text('Tê qeydkirin: ' + summary + '…'); withPresenceShowing(false, function(presence) { if (!$savelog) { $savelog = $('<ul>').css("margin", "0px -10px -10px -10px") .css("padding", "10px"); // jQuery $.css destroys var(...) $savelog[0].style.backgroundColor = "var(--wikt-palette-palegreen, #DDFFDD)"; $(presence).append($savelog); } $savelog.append(saveLi); if (originalText == currentText) return thiz.error("Rûpel nehat guhartin."); else if (!currentText) return thiz.error("Xeta: rûpel vala bû."); }); originalText = currentText; var nst = [] var node; while (node = nodestack.pop()) { nst.push(node); } thiz.page.Save(currentText, { summary: summary.trim(), notminor: true, tags: tags.join('|') }).then(function(res) { if (res == null) return thiz.error("Di qeydkirinê de xetayek çêbû."); try { saveLi.append( $('<span>') .append($("<b>").text("Qeyd bû ")) .append($('<a>').attr("href", mw.config.get('wgScript') + '?title=' + encodeURIComponent(mw.config.get('wgPageName')) + '&diff=' + encodeURIComponent(res.edit.newrevid) + '&oldid=' + encodeURIComponent(res.edit.oldrevid)) .text("(guhartin)"))); } catch (e) { if (res.error) { thiz.error("Qeyd nebû: " + String(res.error.info)); } else { thiz.error($('<p>').text(String(e))[0]); } } for (var i = 0; i < nst.length; i++) nst[i].style.cssText = "background-color: var(--wikt-palette-green-4,#0F0);border: 2px var(--wikt-palette-green-6,#0F0) solid;"; window.setTimeout(function() { var node; while (node = nst.pop()) node.style.cssText = ""; }, 400); // restore any callbacks that were waiting for currentText before we started for (var i = 0; i < cleanup_callbacks.length; i++) thiz.withCurrentText(cleanup_callbacks[i]); }, function(code, data) { thiz.error("Qeyd nebû: " + ((data && data.error && data.error.info) || code)); }); }); } } /** * A small amount of common code that can be usefully applied to adder forms. * * An adder is assumed to be an object that has: * * .fields A object mapping field names to either validation functions used * for text fields, or the word 'checkbox' * * .createForm A function () that returns a newNode('form') to be added to the * document (by appending to insertNode) * * .onsubmit A function (values, register (wikitext, callback)) that accepts * the validated set of values and processes them, the register * function accepts wikitext and a continuation function to be * called with the result of rendering it. * * Before onsubmit or any validation functions are called, but after running * createForm, a new property .elements will be added to the adder which is a * dictionary mapping field names to HTML input elements. * * @param {editor} The current editor. * @param {adder} The relevant adder. * @param {insertNode} Where to insert this in the document. * @param {insertSibling} Where to insert this within insertNode. */ window.AdderWrapper = function(editor, adder, insertNode, insertSibling) { var form = adder.createForm() var status = $('<span>')[0]; form.appendChild(status); if (insertSibling) insertNode.insertBefore(form, insertSibling); else insertNode.appendChild(form); adder.elements = {}; //This is all because IE doesn't reliably allow form.elements['name'] for (var i = 0; i < form.elements.length; i++) { adder.elements[form.elements[i].name] = form.elements[i]; } form.onsubmit = function() { try { var submit = true; var values = {} status.innerHTML = ""; for (var name in adder.fields) { if (adder.fields[name] == 'checkbox') { values[name] = adder.elements[name].checked ? name : false; } else { adder.elements[name].style.border = ''; // clear error styles values[name] = adder.fields[name](adder.elements[name].value || '', function(msg) { var element = $('<span>').append($('<img>').attr('src', 'https://upload.wikimedia.org/wikipedia/commons/4/4e/MW-Icon-AlertMark.png')) .append(msg) .append($('<br>'))[0]; // jQuery $.css destroys var(...) element.style.color = "var(--wikt-palette-red, #ff0000)"; status.appendChild(element); adder.elements[name].style.border = "solid var(--wikt-palette-red,#CC0000) 2px"; return false }); if (values[name] === false) submit = false; } } if (!submit) return false; var loading = $('<span>').text('Tê barkirin…')[0]; status.appendChild(loading); adder.onsubmit(values, function(text, callback) { new mw.Api().parse(text, { title: mw.config.get('wgPageName'), pst: true, //pst makes subst work as expected disablelimitreport: true }) .then(function(r) { var cleanedHtml = $.parseHTML(r)[0].children[0].innerHTML; //first child of .mw-parser-output callback(cleanedHtml); status.removeChild(loading); }).fail(function(r) { if (r) console.log("wikt.translation-adder editor: " + r); var errorElement = $('<p>').text('Têkilî bi serverê re çênebû.')[0]; // jQuery $.css destroys var(...) errorElement.style.color = "var(--wikt-palette-red, #ff0000)"; loading.appendChild(errorElement); }); }); } catch (e) { status.textContent = "Xeta: " + (e.message || e); return false; } return false; } }; // </nowiki> jlq5pi219ztacpwrfifwxa1jzirhhdg 6794065 6793966 2026-10-14T20:17:16Z Wikihez 7344 Placeholders; code prefixes in the language search, common languages first; preview stays on its line; no + on lines 6794065 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: the page editor (edit stack, undo/redo, * save box) and AdderWrapper, which runs an adder's form. Forked from en.wiktionary's * [[en:MediaWiki:Gadget-Editor.js]] (by [[en:User:Conrad.Irwin]]). */ /* global mw, $ */ window.PageEditor = function(title) { this.CheckOutForEdit = function() { return new mw.Api().get({ action: 'query', prop: 'revisions', rvprop: ['ids', 'content', 'timestamp'], titles: String(title), formatversion: '2', curtimestamp: true }) .then(function(data) { var page, revision; if (!data.query || !data.query.pages) { return $.Deferred().reject('unknown'); } page = data.query.pages[0]; if (!page || page.missing) { return $.Deferred().reject('nocreate-missing'); } revision = page.revisions[0]; this.baserevid = revision.revid; this.basetimestamp = revision.timestamp; this.curtimestamp = data.curtimestamp; return revision.content; }); } this.Save = function(newWikitext, params) { var editParams = typeof params === 'object' ? params : { text: String(params) }; return new mw.Api().postWithEditToken($.extend({ action: 'edit', title: title, formatversion: '2', text: newWikitext, // Protect against errors and conflicts assert: mw.user.isAnon() ? undefined : 'user', baserevid: this.baserevid, basetimestamp: this.basetimestamp, starttimestamp: this.curtimestamp, nocreate: true }, editParams)); } } /** * A generic page editor for the current page. * * This is a singleton and it displays a small interface in the top left after * the first edit has been registered. * * @public * this.page * this.addEdit * this.error * */ window.Editor = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; this.page = new PageEditor(mw.config.get('wgPageName')); // get the current text of the article and call the callback with it // NOTE: This function also acts as a loose non-re-entrant lock to protect currentText. this.withCurrentText = function(callback) { if (callbacks.length == 0) { callbacks = [callback]; for (var i = 0; i < callbacks.length; i++) { callbacks[i](currentText); } return callbacks = []; } if (callbacks.length > 0) { return callbacks.push(callback); } callbacks = [callback]; thiz.page.CheckOutForEdit().then(function(wikitext) { if (wikitext === null) return thiz.error("Têkilî bi serverê re çênebû."); currentText = originalText = wikitext; for (var i = 0; i < callbacks.length; i++) { callbacks[i](currentText); } callbacks = []; }); } // A decorator for withCurrentText function performSequentially(f) { return (function() { var the_arguments = arguments; thiz.withCurrentText(function() { f.apply(thiz, the_arguments); }); }); } // add an edit to the editstack function addEdit(edit, node, fromRedo) { withPresenceShowing(false, function() { if (node) { nodestack.push(node); node.style.cssText = "border: 2px var(--wikt-palette-green-4, #00FF00) dashed;" } if (!fromRedo) redostack = []; var ntext = false; try { ntext = edit.edit(currentText); if (ntext && ntext != currentText) { edit.redo(); currentText = ntext; } else return false; } catch (e) { // TODO Uncaught TypeError: Object [object Window] has no method 'error' // I may have just fixed this by changing "this" below to "thiz" ... thiz.error("Xeta: " + e); } editstack.push(edit); }); } this.addEdit = performSequentially(addEdit); // display an error to the user this.error = function(message) { console.trace(message); if (!errorlog) { errorlog = $('<ul>').css("margin", "0px -10px -10px -10px") .css("padding", "10px")[0]; // jQuery $.css destroys var(...) errorlog.style.backgroundColor = "var(--wikt-palette-pink, #FFDDDD)"; withPresenceShowing(true, function(presence) { presence.appendChild(errorlog); }); } errorlog.appendChild($('<li>').text(message)[0]); } var thiz = this; // this is set incorrectly when private functions are used as callbacks. var editstack = []; // A list of the edits that have been applied to get currentText var redostack = []; // A list of the edits that have been recently undone. var nodestack = []; // A lst of nodes to which we have added highlighting var callbacks = {}; // A list of onload callbacks (initially .length == undefined) var originalText = ""; // What was the contents of the page before we fiddled? var currentText = ""; // What is the contents now? var errorlog; // The ul for sticking errors in. var $savelog; // The ul for save messages. //Move an edit from the editstack to the redostack function undo() { if (editstack.length == 0) return false; var edit = editstack.pop(); redostack.push(edit); edit.undo(); var text = originalText; for (var i = 0; i < editstack.length; i++) { var ntext = false; try { ntext = editstack[i].edit(text); } catch (e) { thiz.error("Xeta: " + e); } if (ntext && ntext != text) { text = ntext; } else { editstack[i].undo(); editstack = editstack.splice(0, i); break; } } currentText = text; return true; } this.undo = performSequentially(undo); //Move an edit from the redostack to the editstack function redo() { if (redostack.length == 0) return; var edit = redostack.pop(); addEdit(edit, null, true); } this.redo = performSequentially(redo); function withPresenceShowing(broken, callback) { if (arguments.callee.presence) { if (broken || arguments.callee.presence.querySelector("button")) { arguments.callee.presence.style.display = "block"; return callback(arguments.callee.presence); } else { try { arguments.callee.presence.parentNode.removeChild(arguments.callee.presence); } catch (e) { } } } var presence = $('<div>').css("position", "fixed") .css("top", "0px") .css("left", "0px") .css("z-index", "10") .css("padding", "30px")[0]; // jQuery $.css destroys var(...) presence.style.backgroundColor = "var(--wikt-palette-green-4, #00FF00)"; window.setTimeout(function() { presence.style.backgroundColor = "var(--wikt-palette-dulllightblue, #CCCCFF)"; presence.style.padding = "10px"; }, 400); var closeButton = $('<div>').css("position", "relative") .css("top", "0px") .css("left", "0px") .css("margin", "-10px") .css("cursor", "pointer") .on("click", performSequentially(close)) .text("X")[0]; // jQuery $.css destroys var(...) closeButton.style.color = "var(--wikt-palette-blue, #0000FF)"; presence.appendChild(closeButton); document.body.insertBefore(presence, document.body.firstChild); var contents = $('<p>').css('text-align', 'center') .append($('<b>').text('Guhartina rûpelê')).append('<br>'); if (!broken) { contents.append($('<button>').text("Guhartinan qeyd bike") .attr('title', 'Guhartinên xwe qeyd bike [s]') .attr('accesskey', 's') .on("click", save)); contents.append($('<br>')); contents.append($('<button>').text("Paşve") .attr('title', 'Guhartina dawî paşve bibe [z]') .attr('accesskey', 'z') .on("click", thiz.undo)); contents.append($('<button>').text("Dîsa bike").on('click', thiz.redo)); mw.loader.using('mediawiki.util').then(function() { contents.children().updateTooltipAccessKeys(); }); } presence.appendChild(contents[0]); arguments.callee.presence = presence; callback(presence); } // Remove the button function close() { while (undo()) ; withPresenceShowing(true, function(presence) { presence.style.display = "none"; if (errorlog) { errorlog.parentNode.removeChild(errorlog); errorlog = false; } }); } //Send the currentText back to the server to save. function save() { thiz.withCurrentText(function() { if (editstack.length == 0) return; var cleanup_callbacks = callbacks; callbacks = []; var sum = {}; var tags = []; for (var i = 0; i < editstack.length; i++) { sum[editstack[i].summary] = true; if (editstack[i].after_save) cleanup_callbacks.push(editstack[i].after_save); if (editstack[i].tags) tags = tags.concat(editstack[i].tags.filter(tag => !!tag)); } var summary = ""; for (var name in sum) { summary += name + " "; } editstack = []; redostack = []; var saveLi = $('<li>').text('Tê qeydkirin: ' + summary + '…'); withPresenceShowing(false, function(presence) { if (!$savelog) { $savelog = $('<ul>').css("margin", "0px -10px -10px -10px") .css("padding", "10px"); // jQuery $.css destroys var(...) $savelog[0].style.backgroundColor = "var(--wikt-palette-palegreen, #DDFFDD)"; $(presence).append($savelog); } $savelog.append(saveLi); if (originalText == currentText) return thiz.error("Rûpel nehat guhartin."); else if (!currentText) return thiz.error("Xeta: rûpel vala bû."); }); originalText = currentText; var nst = [] var node; while (node = nodestack.pop()) { nst.push(node); } thiz.page.Save(currentText, { summary: summary.trim(), notminor: true, tags: tags.join('|') }).then(function(res) { if (res == null) return thiz.error("Di qeydkirinê de xetayek çêbû."); try { saveLi.append( $('<span>') .append($("<b>").text("Qeyd bû ")) .append($('<a>').attr("href", mw.config.get('wgScript') + '?title=' + encodeURIComponent(mw.config.get('wgPageName')) + '&diff=' + encodeURIComponent(res.edit.newrevid) + '&oldid=' + encodeURIComponent(res.edit.oldrevid)) .text("(guhartin)"))); } catch (e) { if (res.error) { thiz.error("Qeyd nebû: " + String(res.error.info)); } else { thiz.error($('<p>').text(String(e))[0]); } } for (var i = 0; i < nst.length; i++) nst[i].style.cssText = "background-color: var(--wikt-palette-green-4,#0F0);border: 2px var(--wikt-palette-green-6,#0F0) solid;"; window.setTimeout(function() { var node; while (node = nst.pop()) node.style.cssText = ""; }, 400); // restore any callbacks that were waiting for currentText before we started for (var i = 0; i < cleanup_callbacks.length; i++) thiz.withCurrentText(cleanup_callbacks[i]); }, function(code, data) { thiz.error("Qeyd nebû: " + ((data && data.error && data.error.info) || code)); }); }); } } /** * A small amount of common code that can be usefully applied to adder forms. * * An adder is assumed to be an object that has: * * .fields A object mapping field names to either validation functions used * for text fields, or the word 'checkbox' * * .createForm A function () that returns a newNode('form') to be added to the * document (by appending to insertNode) * * .onsubmit A function (values, register (wikitext, callback)) that accepts * the validated set of values and processes them, the register * function accepts wikitext and a continuation function to be * called with the result of rendering it. * * Before onsubmit or any validation functions are called, but after running * createForm, a new property .elements will be added to the adder which is a * dictionary mapping field names to HTML input elements. * * @param {editor} The current editor. * @param {adder} The relevant adder. * @param {insertNode} Where to insert this in the document. * @param {insertSibling} Where to insert this within insertNode. */ window.AdderWrapper = function(editor, adder, insertNode, insertSibling) { var form = adder.createForm() var status = $('<span>')[0]; form.appendChild(status); if (insertSibling) insertNode.insertBefore(form, insertSibling); else insertNode.appendChild(form); adder.elements = {}; //This is all because IE doesn't reliably allow form.elements['name'] for (var i = 0; i < form.elements.length; i++) { adder.elements[form.elements[i].name] = form.elements[i]; } form.onsubmit = function() { try { var submit = true; var values = {} status.innerHTML = ""; for (var name in adder.fields) { if (adder.fields[name] == 'checkbox') { values[name] = adder.elements[name].checked ? name : false; } else { adder.elements[name].style.border = ''; // clear error styles values[name] = adder.fields[name](adder.elements[name].value || '', function(msg) { var element = $('<span>').append($('<img>').attr('src', 'https://upload.wikimedia.org/wikipedia/commons/4/4e/MW-Icon-AlertMark.png')) .append(msg) .append($('<br>'))[0]; // jQuery $.css destroys var(...) element.style.color = "var(--wikt-palette-red, #ff0000)"; status.appendChild(element); adder.elements[name].style.border = "solid var(--wikt-palette-red,#CC0000) 2px"; return false }); if (values[name] === false) submit = false; } } if (!submit) return false; var loading = $('<span>').text('Tê barkirin…')[0]; status.appendChild(loading); adder.onsubmit(values, function(text, callback) { new mw.Api().parse(text, { title: mw.config.get('wgPageName'), pst: true, //pst makes subst work as expected disablelimitreport: true }) .then(function(r) { // The line's own paragraph: the parser output can wrap it in a <section> as well. var $out = $('<div>').html(r); var $p = $out.find('p').first(); var cleanedHtml = ($p.length ? $p : $out.children().first()).html(); callback(cleanedHtml); status.removeChild(loading); }).fail(function(r) { if (r) console.log("wikt.translation-adder editor: " + r); var errorElement = $('<p>').text('Têkilî bi serverê re çênebû.')[0]; // jQuery $.css destroys var(...) errorElement.style.color = "var(--wikt-palette-red, #ff0000)"; loading.appendChild(errorElement); }); }); } catch (e) { status.textContent = "Xeta: " + (e.message || e); return false; } return false; } }; // </nowiki> fte17yyh1rkdvv0kgw5vu5e9e4vwh34 Bikarhêner:Wikihez/Gadget-test/translation-adder/language-utils.js 2 1359439 6793967 2026-10-14T18:20:26Z Wikihez 7344 Test copy of the translation adder gadget (fork of en.wiktionary's TranslationAdder) 6793967 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: language and script data from * [[Modul:ziman/javascript-interface]], cached for a week by CacheableStorage ([[MediaWiki:Gadget-StorageUtils.js]]). * Forked from en.wiktionary's [[en:MediaWiki:Gadget-LanguageUtils.js]]. */ /* global mw, CacheableStorage */ function expand(call) { return new mw.Api().get({ action: "expandtemplates", format: "json", text: "{{#invoke:ziman/javascript-interface|" + call + "}}", prop: "wikitext" }).then(function (response) { return JSON.parse(response.expandtemplates.wikitext); }); } window.LanguageUtils = function () { this.automaticTranslitCacheableStorage = new CacheableStorage("LanguageUtils-AutomaticTransliteration", function () { return expand("GetLanguagesWithAutomaticTransliteration"); }, 7, 1); // False until the cache is filled, which the first call starts. this.HasAutomaticTransliteration = function (langcode) { var langs = this.automaticTranslitCacheableStorage.GetItem(); return (langs && langs[langcode]) || false; }; }; window.ScriptUtils = function () { this.cacheableStorage = new CacheableStorage("ScriptUtils", function () { return expand("AllLangcodeToScripts"); }, 7, 1); this.GetScriptsByLangCode = function (langcode) { var allData = this.cacheableStorage.GetItem(); if (allData) { var scripts = allData[langcode]; return typeof scripts === "string" ? [scripts] : scripts; } return []; }; }; // </nowiki> 7k7pday5ahqa7z793ajrrl8lyqsxrjr Bikarhêner:Wikihez/Gadget-test/translation-adder/data.js 2 1359440 6793968 2026-10-14T18:20:31Z Wikihez 7344 Test copy of the translation adder gadget (fork of en.wiktionary's TranslationAdder) 6793968 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:werger]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Nesting is given by hand in the form: the wiki has no table of Kurmancî group names. this.getNested = function(lang) { return ""; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> 4hb0t02tddpazb1tz0lfpxlg5knh7s0 6793998 6793968 2026-10-14T18:29:04Z Wikihez 7344 Call back when the interwiki or page name lookup fails 6793998 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:werger]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W-}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Nesting is given by hand in the form: the wiki has no table of Kurmancî group names. this.getNested = function(lang) { return ""; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> 8xsqqan31m4ehs3umg98swvmdnzabse 6794028 6793998 2026-10-14T19:03:35Z Wikihez 7344 Write {{W}} when the other wiktionary has no entry, as en.wiktionary writes {{t}} 6794028 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:translations]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Nesting is given by hand in the form: the wiki has no table of Kurmancî group names. this.getNested = function(lang) { return ""; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> 8ookrls0miqlr2cya408r0zil72uerr 6794044 6794028 2026-10-14T19:33:42Z Wikihez 7344 Translation adder: automatic nesting by code, line editing (✎ ×), highlighted pending lines 6794044 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:translations]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Language -> the language its line goes under (* {{Z|zh}}: / *: {{Z|cmn}}: …), from en.wiktionary's nesting // table with its group names turned into codes; groups that have no code of their own are left out. var nesting = { "abh": "ar", "abv": "ar", "acm": "ar", "acw": "ar", "acx": "ar", "acy": "ar", "adf": "ar", "aeb": "ar", "afb": "ar", "aii": "arc", "aij": "arc", "ajp": "ar", "amw": "arc", "apc": "ar", "apd": "ar", "arq": "ar", "ars": "ar", "ary": "ar", "arz": "ar", "atj": "cr", "auz": "ar", "axm": "hy", "ayl": "ar", "ayn": "ar", "ayp": "ar", "azd": "nah", "azn": "nah", "azz": "nah", "bar": "de", "bgx": "tr", "bhn": "arc", "bhv": "pdu", "bjf": "arc", "bpn": "zh", "bpq": "ms", "bpy": "mni", "btj": "ms", "bve": "ms", "bvu": "ms", "cdo": "zh", "cjy": "zh", "cld": "arc", "cmg": "mn", "cmn": "zh", "cnp": "zh", "cnx": "kw", "cpg": "el", "cpx": "zh", "crj": "cr", "crk": "cr", "crl": "cr", "crm": "cr", "crp-mpp": "pt", "csp": "zh", "csw": "cr", "cwd": "cr", "czh": "zh", "czo": "zh", "daq": "mds", "dib": "din", "dik": "din", "dip": "din", "diw": "din", "dks": "din", "dng": "zh", "doc": "kdx", "dra-mkn": "kn", "dra-okn": "kn", "dra-ote": "te", "dum": "nl", "egx-dem": "egy", "fal": "fli", "fll": "fli", "frm": "fr", "fro": "fr", "gan": "zh", "ggn": "gvr", "gkp": "kpe", "gmh": "de", "gml": "nds", "gmq-mno": "no", "gmq-oda": "da", "gmq-ogt": "gmq-gut", "gmq-osw": "sv", "gmw-cfr": "de", "gmw-ecg": "de", "gmw-jdt": "nl", "gmw-msc": "sco", "gmw-rfr": "de", "gmy": "el", "goh": "de", "grc": "el", "grk-cal": "el", "grk-ita": "el", "grk-mar": "el", "gsw": "de", "hak": "zh", "hax": "hai", "hdn": "hai", "hnm": "zh", "hrt": "arc", "hsn": "zh", "huy": "arc", "ike": "iu", "inc-ash": "pra", "inc-kam": "pra", "inc-mas": "as", "inc-mbn": "bn", "inc-mgu": "gu", "inc-mor": "or", "inc-oas": "as", "inc-oaw": "awa", "inc-obn": "bn", "inc-ogu": "gu", "inc-ohi": "hi", "inc-oor": "or", "inc-opa": "pa", "jas": "jv", "jax": "ms", "jvn": "jv", "kaw": "jv", "klg": "kqe", "kll": "kqe", "kmc": "kdx", "kmz": "tr", "ko-ear": "ko", "kqd": "arc", "kxd": "ms", "kxm": "km", "lhs": "arc", "lrt": "ms", "lsd": "arc", "ltc": "zh", "luh": "zh", "lzh": "zh", "meo": "ms", "mey": "ar", "mfa": "ms", "mga": "ga", "mhp": "ms", "mid": "arc", "mis-hkl": "zh", "mis-tnw": "zh", "mkh-mmn": "mnw", "mkh-mvi": "vi", "mkn": "ms", "mku": "man", "mnk": "man", "mnp": "zh", "mnw-tha": "mnw", "moe": "cr", "mqg": "ms", "mrr": "mds", "msc": "man", "msi": "ms", "myz": "arc", "nan": "zh", "nan-dat": "zh", "nan-hbl": "zh", "nan-hlh": "zh", "nan-lnx": "zh", "nan-tws": "zh", "nan-zhe": "zh", "nan-zsh": "zh", "naz": "nah", "nb": "no", "nch": "nah", "nci": "nah", "ncj": "nah", "ncl": "nah", "ncx": "nah", "nds-de": "nds", "nds-nl": "nds", "ngu": "nah", "nhc": "nah", "nhe": "nah", "nhg": "nah", "nhi": "nah", "nhk": "nah", "nhm": "nah", "nhn": "nah", "nhp": "nah", "nhq": "nah", "nht": "nah", "nhv": "nah", "nhw": "nah", "nhx": "nah", "nhy": "nah", "nhz": "nah", "nlv": "nah", "nn": "no", "nod": "th", "npl": "nah", "nrt": "kyl", "nsk": "cr", "nsl": "no", "nsu": "nah", "nuz": "nah", "nwc": "new", "nwx": "new", "oav": "av", "obr": "my", "obt": "br", "och": "zh", "oco": "kw", "odt": "nl", "oge": "ka", "ohu": "hu", "ojp": "ja", "okm": "ko", "oko": "ko", "okz": "km", "olt": "lt", "omp": "mni", "omr": "mr", "omx": "mnw", "ort": "or", "osn": "su", "osp": "es", "ota": "tr", "otb": "bo", "oty": "ta", "owl": "cy", "pal": "fa", "peo": "fa", "pga": "ar", "pgl": "ga", "pht": "th", "pmu": "pa", "pmy": "ms", "pnt": "el", "poz-sml": "ms", "poz-ter": "ms", "pra-niy": "pra", "pro": "oc", "pse": "ms", "qwc": "qu", "rmc": "rom", "rmf": "rom", "rml": "rom", "rmn": "rom", "rmo": "rom", "rmu": "rom", "rmw": "rom", "rmy": "rom", "roa-oca": "ca", "roa-ole": "roa-leo", "sam": "arc", "sbd": "smq", "sga": "ga", "shu": "ar", "sjc": "zh", "slm": "smd", "sml": "smd", "smp": "he", "sou": "th", "ssb": "smd", "ssh": "ar", "stj": "smq", "swg": "de", "sxk": "kyl", "syc": "arc", "sym": "smq", "syn": "arc", "trg": "arc", "trk-oat": "tr", "tru": "arc", "tsd": "el", "vkp": "pt", "vkt": "ms", "vmf": "de", "wlm": "cy", "wuu": "zh", "wxa": "zh", "xaa": "ar", "xbm": "br", "xcl": "hy", "xct": "bo", "xhm": "km", "xmm": "ms", "xpe": "kpe", "xzp": "zap", "yej": "el", "yue": "zh", "zaa": "zap", "zab": "zap", "zac": "zap", "zad": "zap", "zae": "zap", "zaf": "zap", "zai": "zap", "zam": "zap", "zao": "zap", "zaq": "zap", "zar": "zap", "zas": "zap", "zat": "zap", "zav": "zap", "zaw": "zap", "zax": "zap", "zca": "zap", "zhx-sht": "zh", "zhx-sic": "zh", "zhx-tai": "zh", "zls-chs": "cu", "zlw-ocs": "cs", "zlw-opl": "pl", "zlw-osk": "sk", "zmi": "ms", "zoo": "zap", "zpa": "zap", "zpb": "zap", "zpc": "zap", "zpd": "zap", "zpe": "zap", "zpf": "zap", "zpg": "zap", "zph": "zap", "zpi": "zap", "zpj": "zap", "zpk": "zap", "zpl": "zap", "zpm": "zap", "zpn": "zap", "zpo": "zap", "zpp": "zap", "zpq": "zap", "zpr": "zap", "zps": "zap", "zpt": "zap", "zpu": "zap", "zpv": "zap", "zpw": "zap", "zpx": "zap", "zpy": "zap", "zpz": "zap", "zsr": "zap", "zte": "zap", "ztg": "zap", "ztl": "zap", "ztm": "zap", "ztn": "zap", "ztp": "zap", "ztq": "zap", "zts": "zap", "ztt": "zap", "ztu": "zap", "ztx": "zap", "zty": "zap" }; // The code of the language this one is nested under, or "". this.getNested = function(lang) { return nesting[lang] || ""; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> q71v4os2emen6biq6667tfw75u75w4d 6794066 6794044 2026-10-14T20:17:21Z Wikihez 7344 Placeholders; code prefixes in the language search, common languages first; preview stays on its line; no + on lines 6794066 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; // Whether the language is one translations are often given in (it has an entry in the table above). this.isCommon = function(lang) { return Object.prototype.hasOwnProperty.call(metadata, lang); }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:translations]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Language -> the language its line goes under (* {{Z|zh}}: / *: {{Z|cmn}}: …), from en.wiktionary's nesting // table with its group names turned into codes; groups that have no code of their own are left out. var nesting = { "abh": "ar", "abv": "ar", "acm": "ar", "acw": "ar", "acx": "ar", "acy": "ar", "adf": "ar", "aeb": "ar", "afb": "ar", "aii": "arc", "aij": "arc", "ajp": "ar", "amw": "arc", "apc": "ar", "apd": "ar", "arq": "ar", "ars": "ar", "ary": "ar", "arz": "ar", "atj": "cr", "auz": "ar", "axm": "hy", "ayl": "ar", "ayn": "ar", "ayp": "ar", "azd": "nah", "azn": "nah", "azz": "nah", "bar": "de", "bgx": "tr", "bhn": "arc", "bhv": "pdu", "bjf": "arc", "bpn": "zh", "bpq": "ms", "bpy": "mni", "btj": "ms", "bve": "ms", "bvu": "ms", "cdo": "zh", "cjy": "zh", "cld": "arc", "cmg": "mn", "cmn": "zh", "cnp": "zh", "cnx": "kw", "cpg": "el", "cpx": "zh", "crj": "cr", "crk": "cr", "crl": "cr", "crm": "cr", "crp-mpp": "pt", "csp": "zh", "csw": "cr", "cwd": "cr", "czh": "zh", "czo": "zh", "daq": "mds", "dib": "din", "dik": "din", "dip": "din", "diw": "din", "dks": "din", "dng": "zh", "doc": "kdx", "dra-mkn": "kn", "dra-okn": "kn", "dra-ote": "te", "dum": "nl", "egx-dem": "egy", "fal": "fli", "fll": "fli", "frm": "fr", "fro": "fr", "gan": "zh", "ggn": "gvr", "gkp": "kpe", "gmh": "de", "gml": "nds", "gmq-mno": "no", "gmq-oda": "da", "gmq-ogt": "gmq-gut", "gmq-osw": "sv", "gmw-cfr": "de", "gmw-ecg": "de", "gmw-jdt": "nl", "gmw-msc": "sco", "gmw-rfr": "de", "gmy": "el", "goh": "de", "grc": "el", "grk-cal": "el", "grk-ita": "el", "grk-mar": "el", "gsw": "de", "hak": "zh", "hax": "hai", "hdn": "hai", "hnm": "zh", "hrt": "arc", "hsn": "zh", "huy": "arc", "ike": "iu", "inc-ash": "pra", "inc-kam": "pra", "inc-mas": "as", "inc-mbn": "bn", "inc-mgu": "gu", "inc-mor": "or", "inc-oas": "as", "inc-oaw": "awa", "inc-obn": "bn", "inc-ogu": "gu", "inc-ohi": "hi", "inc-oor": "or", "inc-opa": "pa", "jas": "jv", "jax": "ms", "jvn": "jv", "kaw": "jv", "klg": "kqe", "kll": "kqe", "kmc": "kdx", "kmz": "tr", "ko-ear": "ko", "kqd": "arc", "kxd": "ms", "kxm": "km", "lhs": "arc", "lrt": "ms", "lsd": "arc", "ltc": "zh", "luh": "zh", "lzh": "zh", "meo": "ms", "mey": "ar", "mfa": "ms", "mga": "ga", "mhp": "ms", "mid": "arc", "mis-hkl": "zh", "mis-tnw": "zh", "mkh-mmn": "mnw", "mkh-mvi": "vi", "mkn": "ms", "mku": "man", "mnk": "man", "mnp": "zh", "mnw-tha": "mnw", "moe": "cr", "mqg": "ms", "mrr": "mds", "msc": "man", "msi": "ms", "myz": "arc", "nan": "zh", "nan-dat": "zh", "nan-hbl": "zh", "nan-hlh": "zh", "nan-lnx": "zh", "nan-tws": "zh", "nan-zhe": "zh", "nan-zsh": "zh", "naz": "nah", "nb": "no", "nch": "nah", "nci": "nah", "ncj": "nah", "ncl": "nah", "ncx": "nah", "nds-de": "nds", "nds-nl": "nds", "ngu": "nah", "nhc": "nah", "nhe": "nah", "nhg": "nah", "nhi": "nah", "nhk": "nah", "nhm": "nah", "nhn": "nah", "nhp": "nah", "nhq": "nah", "nht": "nah", "nhv": "nah", "nhw": "nah", "nhx": "nah", "nhy": "nah", "nhz": "nah", "nlv": "nah", "nn": "no", "nod": "th", "npl": "nah", "nrt": "kyl", "nsk": "cr", "nsl": "no", "nsu": "nah", "nuz": "nah", "nwc": "new", "nwx": "new", "oav": "av", "obr": "my", "obt": "br", "och": "zh", "oco": "kw", "odt": "nl", "oge": "ka", "ohu": "hu", "ojp": "ja", "okm": "ko", "oko": "ko", "okz": "km", "olt": "lt", "omp": "mni", "omr": "mr", "omx": "mnw", "ort": "or", "osn": "su", "osp": "es", "ota": "tr", "otb": "bo", "oty": "ta", "owl": "cy", "pal": "fa", "peo": "fa", "pga": "ar", "pgl": "ga", "pht": "th", "pmu": "pa", "pmy": "ms", "pnt": "el", "poz-sml": "ms", "poz-ter": "ms", "pra-niy": "pra", "pro": "oc", "pse": "ms", "qwc": "qu", "rmc": "rom", "rmf": "rom", "rml": "rom", "rmn": "rom", "rmo": "rom", "rmu": "rom", "rmw": "rom", "rmy": "rom", "roa-oca": "ca", "roa-ole": "roa-leo", "sam": "arc", "sbd": "smq", "sga": "ga", "shu": "ar", "sjc": "zh", "slm": "smd", "sml": "smd", "smp": "he", "sou": "th", "ssb": "smd", "ssh": "ar", "stj": "smq", "swg": "de", "sxk": "kyl", "syc": "arc", "sym": "smq", "syn": "arc", "trg": "arc", "trk-oat": "tr", "tru": "arc", "tsd": "el", "vkp": "pt", "vkt": "ms", "vmf": "de", "wlm": "cy", "wuu": "zh", "wxa": "zh", "xaa": "ar", "xbm": "br", "xcl": "hy", "xct": "bo", "xhm": "km", "xmm": "ms", "xpe": "kpe", "xzp": "zap", "yej": "el", "yue": "zh", "zaa": "zap", "zab": "zap", "zac": "zap", "zad": "zap", "zae": "zap", "zaf": "zap", "zai": "zap", "zam": "zap", "zao": "zap", "zaq": "zap", "zar": "zap", "zas": "zap", "zat": "zap", "zav": "zap", "zaw": "zap", "zax": "zap", "zca": "zap", "zhx-sht": "zh", "zhx-sic": "zh", "zhx-tai": "zh", "zls-chs": "cu", "zlw-ocs": "cs", "zlw-opl": "pl", "zlw-osk": "sk", "zmi": "ms", "zoo": "zap", "zpa": "zap", "zpb": "zap", "zpc": "zap", "zpd": "zap", "zpe": "zap", "zpf": "zap", "zpg": "zap", "zph": "zap", "zpi": "zap", "zpj": "zap", "zpk": "zap", "zpl": "zap", "zpm": "zap", "zpn": "zap", "zpo": "zap", "zpp": "zap", "zpq": "zap", "zpr": "zap", "zps": "zap", "zpt": "zap", "zpu": "zap", "zpv": "zap", "zpw": "zap", "zpx": "zap", "zpy": "zap", "zpz": "zap", "zsr": "zap", "zte": "zap", "ztg": "zap", "ztl": "zap", "ztm": "zap", "ztn": "zap", "ztp": "zap", "ztq": "zap", "zts": "zap", "ztt": "zap", "ztu": "zap", "ztx": "zap", "zty": "zap" }; // The code of the language this one is nested under, or "". this.getNested = function(lang) { return nesting[lang] || ""; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> 2ra3u6tclr0l2bqjy31zmi7hjc2u35o 6794073 6794066 2026-10-14T20:27:50Z Wikihez 7344 Nesting under families from Modul:malbat for en.wiktionary's groups that have no language code 6794073 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; // Whether the language is one translations are often given in (it has an entry in the table above). this.isCommon = function(lang) { return Object.prototype.hasOwnProperty.call(metadata, lang); }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:translations]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Language -> the language or family its line goes under (* {{Z|zh}}: / *: {{Z|cmn}}: …), from en.wiktionary's // nesting table with its group names turned into codes: a language's, else a family's from [[Modul:malbat]]. // Groups with neither are left out. var nesting = { "abh": "ar", "abv": "ar", "acm": "ar", "acw": "ar", "acx": "ar", "acy": "ar", "adf": "ar", "aeb": "ar", "aee": "inc-pas", "afb": "ar", "ahi": "kro-aiz", "ahm": "kro-aiz", "ahp": "kro-aiz", "aii": "arc", "aij": "arc", "ajp": "ar", "akb": "btk", "amw": "arc", "aou": "gio", "apc": "ar", "apd": "ar", "aqd": "qfa-dgn", "arq": "ar", "ars": "ar", "ary": "ar", "arz": "ar", "atd": "mno", "atj": "cr", "auz": "ar", "axm": "hy", "ayl": "ar", "ayn": "ar", "ayp": "ar", "azd": "nah", "azn": "nah", "azz": "nah", "bar": "de", "bbc": "btk", "bca": "sit-bai", "bet": "kro-bet", "bev": "kro-bet", "bfc": "sit-bai", "bfs": "sit-bai", "bgo": "alv-bag", "bgx": "tr", "bhn": "arc", "bhv": "pdu", "bjf": "arc", "blk": "kar", "bmd": "alv-bag", "bpn": "zh", "bpq": "ms", "bpy": "mni", "bqf": "alv-bag", "bsp": "alv-bag", "bsv": "alv-bag", "btd": "btk", "btg": "kro-bet", "btj": "ms", "btm": "btk", "bts": "btk", "btx": "btk", "btz": "btk", "bve": "ms", "bvu": "ms", "bwe": "kar", "bya": "btk", "caq": "aav-nic", "cbj": "alv-ede", "cdo": "zh", "cib": "alv-gbe", "cjy": "zh", "cld": "arc", "cly": "omq-cha", "cmg": "mn", "cmn": "zh", "cnp": "zh", "cnx": "kw", "cpg": "el", "cpx": "zh", "crj": "cr", "crk": "cr", "crl": "cr", "crm": "cr", "crp-mpp": "pt", "csp": "zh", "csw": "cr", "cta": "omq-cha", "ctp": "omq-cha", "ctz": "omq-cha", "cut": "omq-cui", "cux": "omq-cui", "cwd": "cr", "cya": "omq-cha", "czh": "zh", "czn": "omq-cha", "czo": "zh", "daq": "mds", "dbg": "qfa-dgn", "dbt": "qfa-dgn", "dbu": "qfa-dgn", "dbw": "qfa-dgn", "dds": "qfa-dgn", "dgb": "qfa-dgn", "dib": "din", "dic": "kro-did", "dik": "din", "dip": "din", "diw": "din", "djm": "qfa-dgn", "dks": "din", "dmb": "qfa-dgn", "dng": "zh", "doc": "kdx", "dra-mkn": "kn", "dra-okn": "kn", "dra-ote": "te", "dsb": "wen", "dti": "qfa-dgn", "dtk": "qfa-dgn", "dtm": "qfa-dgn", "dto": "qfa-dgn", "dts": "qfa-dgn", "dtt": "qfa-dgn", "dtu": "qfa-dgn", "dum": "nl", "dym": "qfa-dgn", "egx-dem": "egy", "emu": "dra-mur", "enc": "qfa-buy", "fal": "fli", "fll": "fli", "fmu": "dra-mur", "frm": "fr", "fro": "fr", "frr": "gmw-fri", "fy": "gmw-fri", "gan": "zh", "gbh": "alv-gbe", "gbo": "kro-grb", "gbs": "alv-gbe", "gbx": "alv-gbe", "gec": "kro-grb", "ggn": "gvr", "ghk": "kar", "gie": "kro-did", "giq": "gio", "gir": "gio", "giu": "gio", "giw": "gio", "gkp": "kpe", "glh": "inc-pas", "gmh": "de", "gml": "nds", "gmq-mno": "no", "gmq-oda": "da", "gmq-ogt": "gmq-gut", "gmq-osw": "sv", "gmw-cfr": "de", "gmw-ecg": "de", "gmw-jdt": "nl", "gmw-msc": "sco", "gmw-rfr": "de", "gmy": "el", "goh": "de", "gqu": "gio", "grc": "el", "grj": "kro-grb", "grk-cal": "el", "grk-ita": "el", "grk-mar": "el", "grv": "kro-grb", "gry": "kro-grb", "gsw": "de", "gud": "kro-did", "gxx": "kro-wee", "hak": "zh", "hax": "hai", "hdn": "hai", "hnm": "zh", "hrt": "arc", "hsb": "wen", "hsn": "zh", "huy": "arc", "ica": "alv-ede", "idd": "alv-ede", "ife": "alv-ede", "ijj": "alv-ede", "ike": "iu", "inc-ash": "pra", "inc-kam": "pra", "inc-mas": "as", "inc-mbn": "bn", "inc-mgu": "gu", "inc-mor": "or", "inc-oas": "as", "inc-oaw": "awa", "inc-obn": "bn", "inc-ogu": "gu", "inc-ohi": "hi", "inc-oor": "or", "inc-opa": "pa", "jas": "jv", "jax": "ms", "jiu": "tbq-jin", "jiy": "tbq-jin", "jkp": "kar", "jmx": "omq-mxt", "jvn": "jv", "kaw": "jv", "kca-eas": "kca", "kca-nor": "kca", "kca-sou": "kca", "kfd": "dra-kor", "kgj": "sit-kha", "kif": "sit-kha", "kip": "sit-kha", "kjl": "sit-kha", "klg": "kqe", "kll": "kqe", "kmc": "kdx", "kmz": "tr", "ko-ear": "ko", "kqd": "arc", "kqk": "alv-gbe", "kqs": "alv-kis", "kss": "alv-kis", "ksw": "kar", "kvl": "kar", "kvq": "kar", "kvt": "kar", "kvu": "kar", "kvy": "kar", "kxd": "ms", "kxf": "kar", "kxk": "kar", "kxm": "km", "kza": "alv-krb", "kzc": "alv-kul", "lay": "sit-bai", "lhs": "arc", "lia": "alv-lim", "lma": "alv-lim", "lrt": "ms", "lsd": "arc", "ltc": "zh", "luh": "zh", "lzh": "zh", "maa": "omq-maz", "mab": "omq-mxt", "maj": "omq-maz", "maq": "omq-maz", "mau": "omq-maz", "mbb": "mno", "mbd": "mno", "mbi": "mno", "mbs": "mno", "mbt": "mno", "mbz": "omq-mxt", "mce": "omq-mxt", "mdv": "omq-mxt", "meh": "omq-mxt", "meo": "ms", "mey": "ar", "mfa": "ms", "mga": "ga", "mhp": "ms", "mhr": "chm", "mib": "omq-mxt", "mid": "arc", "mie": "omq-mxt", "mig": "omq-mxt", "mih": "omq-mxt", "mii": "omq-mxt", "mil": "omq-mxt", "mim": "omq-mxt", "mio": "omq-mxt", "mip": "omq-mxt", "mis-hkl": "zh", "mis-tnw": "zh", "mit": "omq-mxt", "miu": "omq-mxt", "mix": "omq-mxt", "miy": "omq-mxt", "miz": "omq-mxt", "mjc": "omq-mxt", "mkh-mmn": "mnw", "mkh-mvi": "vi", "mkn": "ms", "mks": "omq-mxt", "mku": "man", "mkx": "mno", "mnk": "man", "mnp": "zh", "mns-cen": "mns", "mns-nor": "mns", "mns-sou": "mns", "mnw-tha": "mnw", "moe": "cr", "mpm": "omq-mxt", "mqg": "ms", "mqh": "omq-mxt", "mqk": "mno", "mrj": "chm", "mrr": "mds", "msc": "man", "msi": "ms", "msm": "mno", "mta": "mno", "mtu": "omq-mxt", "mtx": "omq-mxt", "mut": "dra-mur", "mvg": "omq-mxt", "mxa": "omq-mxt", "mxb": "omq-mxt", "mxl": "alv-gbe", "mxs": "omq-mxt", "mxt": "omq-mxt", "mxv": "omq-mxt", "mxy": "omq-mxt", "myz": "arc", "mza": "omq-mxt", "mzi": "omq-maz", "nan": "zh", "nan-dat": "zh", "nan-hbl": "zh", "nan-hlh": "zh", "nan-lnx": "zh", "nan-tws": "zh", "nan-zhe": "zh", "nan-zsh": "zh", "naz": "nah", "nb": "no", "nbw": "nic-ngd", "ncb": "aav-nic", "nch": "nah", "nci": "nah", "ncj": "nah", "ncl": "nah", "ncx": "nah", "nds-de": "nds", "nds-nl": "nds", "ngb": "nic-ngd", "ngu": "nah", "nhc": "nah", "nhe": "nah", "nhg": "nah", "nhi": "nah", "nhk": "nah", "nhm": "nah", "nhn": "nah", "nhp": "nah", "nhq": "nah", "nht": "nah", "nhv": "nah", "nhw": "nah", "nhx": "nah", "nhy": "nah", "nhz": "nah", "nik": "aav-nic", "nku": "alv-kul", "nlv": "nah", "nn": "no", "nod": "th", "nos": "tbq-nis", "npl": "nah", "nqg": "alv-ede", "nqk": "alv-ede", "nrt": "kyl", "nsd": "tbq-nis", "nsk": "cr", "nsl": "no", "nsu": "nah", "nsv": "tbq-nis", "nuz": "nah", "nwc": "new", "nwx": "new", "nzz": "qfa-dgn", "oav": "av", "obo": "mno", "obr": "my", "obt": "br", "och": "zh", "oco": "kw", "odt": "nl", "ofs": "gmw-fri", "oge": "ka", "ohu": "hu", "ojp": "ja", "okm": "ko", "oko": "ko", "okz": "km", "olt": "lt", "omp": "mni", "omq-sjq": "omq-cha", "omq-tel": "omq-mxt", "omq-teo": "omq-cha", "omr": "mr", "omx": "mnw", "ort": "or", "osn": "su", "osp": "es", "ota": "tr", "otb": "bo", "ote": "oto-otm", "otl": "oto-otm", "otm": "oto-otm", "otn": "oto-otm", "otq": "oto-otm", "ots": "oto-otm", "ott": "oto-otm", "otx": "oto-otm", "oty": "ta", "otz": "oto-otm", "owl": "cy", "pal": "fa", "pbm": "omq-maz", "peo": "fa", "pga": "ar", "pgl": "ga", "pht": "th", "pmu": "pa", "pmy": "ms", "pnt": "el", "poz-sml": "ms", "poz-ter": "ms", "pra-niy": "pra", "pro": "oc", "pse": "ms", "psh": "inc-pas", "psi": "inc-pas", "qwc": "qu", "rmc": "rom", "rmf": "rom", "rml": "rom", "rmn": "rom", "rmo": "rom", "rmu": "rom", "rmw": "rom", "rmy": "rom", "roa-oca": "ca", "roa-ole": "roa-leo", "sam": "arc", "sbd": "smq", "scs": "den", "se": "smi", "sel-nor": "sel", "sel-sou": "sel", "sga": "ga", "shu": "ar", "sia": "smi", "sjc": "zh", "sjd": "smi", "sje": "smi", "sjk": "smi", "sjt": "smi", "sju": "smi", "slm": "smd", "sma": "smi", "smj": "smi", "sml": "smd", "smn": "smi", "smp": "he", "sms": "smi", "sou": "th", "ssb": "smd", "ssh": "ar", "stj": "smq", "stq": "gmw-fri", "swb": "bnt-com", "swg": "de", "sxk": "kyl", "sxw": "alv-gbe", "syc": "arc", "sym": "smq", "syn": "arc", "tde": "qfa-dgn", "tfi": "alv-gbe", "the": "inc-tha", "thl": "inc-tha", "thq": "inc-tha", "thr": "inc-tha", "tji": "sit-tja", "tjs": "sit-tja", "tkt": "inc-tha", "trc": "omq-tri", "trg": "arc", "trk-oat": "tr", "trq": "omq-tri", "trs": "omq-tri", "tru": "arc", "tsd": "el", "vkp": "pt", "vkt": "ms", "vmc": "omq-mxt", "vmd": "dra-kor", "vmf": "de", "vmj": "omq-mxt", "vmm": "omq-mxt", "vmp": "omq-maz", "vmq": "omq-mxt", "vmx": "omq-mxt", "vmy": "omq-maz", "vmz": "omq-maz", "wci": "alv-gbe", "wec": "kro-wee", "wem": "alv-gbe", "wlc": "bnt-com", "wlm": "cy", "wni": "bnt-com", "wuu": "zh", "wxa": "zh", "xaa": "ar", "xbm": "br", "xcl": "hy", "xct": "bo", "xhm": "km", "xkb": "alv-ede", "xme-mid": "xme", "xme-old": "xme", "xmm": "ms", "xpe": "kpe", "xrb": "alv-krb", "xsl": "den", "xta": "omq-mxt", "xtb": "omq-mxt", "xtd": "omq-mxt", "xti": "omq-mxt", "xtj": "omq-mxt", "xtl": "omq-mxt", "xtm": "omq-mxt", "xtn": "omq-mxt", "xtp": "omq-mxt", "xts": "omq-mxt", "xtt": "omq-mxt", "xtu": "omq-mxt", "xty": "omq-mxt", "xwe": "alv-gbe", "xwl": "alv-gbe", "xzp": "zap", "yej": "el", "yhl": "tbq-phw", "yik": "tbq-lal", "yit": "tbq-lal", "yiv": "tbq-nis", "ykg": "qfa-yuk", "yln": "qfa-buy", "ymc": "tbq-muj", "ymq": "tbq-muj", "ymx": "tbq-muj", "yok-bvy": "yok", "yok-dly": "yok", "yok-gsy": "yok", "yok-kry": "yok", "yok-nvy": "yok", "yok-ply": "yok", "yok-svy": "yok", "yok-tky": "yok", "ypb": "tbq-phw", "ypn": "tbq-phw", "yrn": "qfa-buy", "yue": "zh", "yux": "qfa-yuk", "ywl": "tbq-lal", "ywt": "tbq-lal", "yzg": "qfa-buy", "zaa": "zap", "zab": "zap", "zac": "zap", "zad": "zap", "zae": "zap", "zaf": "zap", "zai": "zap", "zam": "zap", "zao": "zap", "zaq": "zap", "zar": "zap", "zas": "zap", "zat": "zap", "zav": "zap", "zaw": "zap", "zax": "zap", "zca": "zap", "zdj": "bnt-com", "zhx-sht": "zh", "zhx-sic": "zh", "zhx-tai": "zh", "zls-chs": "cu", "zlw-ocs": "cs", "zlw-opl": "pl", "zlw-osk": "sk", "zmi": "ms", "zoo": "zap", "zpa": "zap", "zpb": "zap", "zpc": "zap", "zpd": "zap", "zpe": "zap", "zpf": "zap", "zpg": "zap", "zph": "zap", "zpi": "zap", "zpj": "zap", "zpk": "zap", "zpl": "zap", "zpm": "zap", "zpn": "zap", "zpo": "zap", "zpp": "zap", "zpq": "zap", "zpr": "zap", "zps": "zap", "zpt": "zap", "zpu": "zap", "zpv": "zap", "zpw": "zap", "zpx": "zap", "zpy": "zap", "zpz": "zap", "zsr": "zap", "zte": "zap", "ztg": "zap", "ztl": "zap", "ztm": "zap", "ztn": "zap", "ztp": "zap", "ztq": "zap", "zts": "zap", "ztt": "zap", "ztu": "zap", "ztx": "zap", "zty": "zap" }; // Kurmancî names of the families in nesting, from [[Modul:malbat/code to canonical name]]. var groupNames = { "aav-nic": "nîkobarî", "alv-bag": "baga", "alv-ede": "ede", "alv-gbe": "gbe", "alv-kis": "kissî", "alv-krb": "karaboro", "alv-kul": "kulango", "alv-lim": "lîmba", "bnt-com": "komorî", "btk": "batakî", "chm": "marî", "den": "slavey", "dra-kor": "koraga", "dra-mur": "muriya", "gio": "gelaoyî", "gmw-fri": "frîsî", "inc-pas": "paşayî", "inc-tha": "tharu", "kar": "karenî", "kca": "xantî", "kro-aiz": "ayizî", "kro-bet": "bétéyî", "kro-did": "didayî", "kro-grb": "greboyî", "kro-wee": "wiyî", "mno": "manoboyî", "mns": "mansî", "nic-ngd": "nîgbandî", "omq-cha": "çatînoyî", "omq-cui": "kuyikatekî", "omq-maz": "mazatekanî", "omq-mxt": "mîkstekî", "omq-tri": "trîkiyî", "oto-otm": "otomî", "qfa-buy": "buyangî", "qfa-dgn": "dogonî", "qfa-yuk": "yukaghirî", "sel": "selkupî", "sit-bai": "bayî", "sit-kha": "khamî", "sit-tja": "tujya", "smi": "samîkî", "tbq-jin": "jîno", "tbq-lal": "laloyî", "tbq-muj": "mujî", "tbq-nis": "nîsu", "tbq-phw": "phowa", "wen": "sorbî", "xme": "medî", "yok": "yokutsanî" }; // The code of the language this one is nested under, or "". this.getNested = function(lang) { return nesting[lang] || ""; }; // The Kurmancî name of a family that languages are nested under, or undefined. this.getGroupName = function(code) { return groupNames[code]; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> azoh4ydlv424cuxn2pajlrv4hgml2xv 6794074 6794073 2026-10-14T20:30:32Z Wikihez 7344 Nesting: Apache, Berber and Sardinian groups as ku pages write them 6794074 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; // Whether the language is one translations are often given in (it has an entry in the table above). this.isCommon = function(lang) { return Object.prototype.hasOwnProperty.call(metadata, lang); }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:translations]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Language -> the language or family its line goes under (* {{Z|zh}}: / *: {{Z|cmn}}: …), from en.wiktionary's // nesting table with its group names turned into codes: a language's, else a family's from [[Modul:malbat]]. // Groups with neither are left out. Apache (apa), Berber (ber) and Sardinian (sc) are as ku pages write them. var nesting = { "abh": "ar", "abv": "ar", "acm": "ar", "acw": "ar", "acx": "ar", "acy": "ar", "adf": "ar", "aeb": "ar", "aee": "inc-pas", "afb": "ar", "ahi": "kro-aiz", "ahm": "kro-aiz", "ahp": "kro-aiz", "aii": "arc", "aij": "arc", "ajp": "ar", "akb": "btk", "amw": "arc", "aou": "gio", "apc": "ar", "apd": "ar", "apj": "apa", "apk": "apa", "apl": "apa", "apm": "apa", "apw": "apa", "aqd": "qfa-dgn", "arq": "ar", "ars": "ar", "ary": "ar", "arz": "ar", "atd": "mno", "atj": "cr", "auz": "ar", "axm": "hy", "ayl": "ar", "ayn": "ar", "ayp": "ar", "azd": "nah", "azn": "nah", "azz": "nah", "bar": "de", "bbc": "btk", "bca": "sit-bai", "bet": "kro-bet", "bev": "kro-bet", "bfc": "sit-bai", "bfs": "sit-bai", "bgo": "alv-bag", "bgx": "tr", "bhn": "arc", "bhv": "pdu", "bjf": "arc", "blk": "kar", "bmd": "alv-bag", "bpn": "zh", "bpq": "ms", "bpy": "mni", "bqf": "alv-bag", "bsp": "alv-bag", "bsv": "alv-bag", "btd": "btk", "btg": "kro-bet", "btj": "ms", "btm": "btk", "bts": "btk", "btx": "btk", "btz": "btk", "bve": "ms", "bvu": "ms", "bwe": "kar", "bya": "btk", "caq": "aav-nic", "cbj": "alv-ede", "cdo": "zh", "cib": "alv-gbe", "cjy": "zh", "cld": "arc", "cly": "omq-cha", "cmg": "mn", "cmn": "zh", "cnp": "zh", "cnx": "kw", "cpg": "el", "cpx": "zh", "crj": "cr", "crk": "cr", "crl": "cr", "crm": "cr", "crp-mpp": "pt", "csp": "zh", "csw": "cr", "cta": "omq-cha", "ctp": "omq-cha", "ctz": "omq-cha", "cut": "omq-cui", "cux": "omq-cui", "cwd": "cr", "cya": "omq-cha", "czh": "zh", "czn": "omq-cha", "czo": "zh", "daq": "mds", "dbg": "qfa-dgn", "dbt": "qfa-dgn", "dbu": "qfa-dgn", "dbw": "qfa-dgn", "dds": "qfa-dgn", "dgb": "qfa-dgn", "dib": "din", "dic": "kro-did", "dik": "din", "dip": "din", "diw": "din", "djm": "qfa-dgn", "dks": "din", "dmb": "qfa-dgn", "dng": "zh", "doc": "kdx", "dra-mkn": "kn", "dra-okn": "kn", "dra-ote": "te", "dsb": "wen", "dti": "qfa-dgn", "dtk": "qfa-dgn", "dtm": "qfa-dgn", "dto": "qfa-dgn", "dts": "qfa-dgn", "dtt": "qfa-dgn", "dtu": "qfa-dgn", "dum": "nl", "dym": "qfa-dgn", "egx-dem": "egy", "emu": "dra-mur", "enc": "qfa-buy", "fal": "fli", "fll": "fli", "fmu": "dra-mur", "frm": "fr", "fro": "fr", "frr": "gmw-fri", "fy": "gmw-fri", "gan": "zh", "gbh": "alv-gbe", "gbo": "kro-grb", "gbs": "alv-gbe", "gbx": "alv-gbe", "gec": "kro-grb", "ggn": "gvr", "ghk": "kar", "gie": "kro-did", "giq": "gio", "gir": "gio", "giu": "gio", "giw": "gio", "gkp": "kpe", "glh": "inc-pas", "gmh": "de", "gml": "nds", "gmq-mno": "no", "gmq-oda": "da", "gmq-ogt": "gmq-gut", "gmq-osw": "sv", "gmw-cfr": "de", "gmw-ecg": "de", "gmw-jdt": "nl", "gmw-msc": "sco", "gmw-rfr": "de", "gmy": "el", "goh": "de", "gqu": "gio", "grc": "el", "grj": "kro-grb", "grk-cal": "el", "grk-ita": "el", "grk-mar": "el", "grv": "kro-grb", "gry": "kro-grb", "gsw": "de", "gud": "kro-did", "gxx": "kro-wee", "hak": "zh", "hax": "hai", "hdn": "hai", "hnm": "zh", "hrt": "arc", "hsb": "wen", "hsn": "zh", "huy": "arc", "ica": "alv-ede", "idd": "alv-ede", "ife": "alv-ede", "ijj": "alv-ede", "ike": "iu", "inc-ash": "pra", "inc-kam": "pra", "inc-mas": "as", "inc-mbn": "bn", "inc-mgu": "gu", "inc-mor": "or", "inc-oas": "as", "inc-oaw": "awa", "inc-obn": "bn", "inc-ogu": "gu", "inc-ohi": "hi", "inc-oor": "or", "inc-opa": "pa", "jas": "jv", "jax": "ms", "jiu": "tbq-jin", "jiy": "tbq-jin", "jkp": "kar", "jmx": "omq-mxt", "jvn": "jv", "kab": "ber", "kaw": "jv", "kca-eas": "kca", "kca-nor": "kca", "kca-sou": "kca", "kfd": "dra-kor", "kgj": "sit-kha", "kif": "sit-kha", "kip": "sit-kha", "kjl": "sit-kha", "klg": "kqe", "kll": "kqe", "kmc": "kdx", "kmz": "tr", "ko-ear": "ko", "kqd": "arc", "kqk": "alv-gbe", "kqs": "alv-kis", "kss": "alv-kis", "ksw": "kar", "kvl": "kar", "kvq": "kar", "kvt": "kar", "kvu": "kar", "kvy": "kar", "kxd": "ms", "kxf": "kar", "kxk": "kar", "kxm": "km", "kza": "alv-krb", "kzc": "alv-kul", "lay": "sit-bai", "lhs": "arc", "lia": "alv-lim", "lma": "alv-lim", "lrt": "ms", "lsd": "arc", "ltc": "zh", "luh": "zh", "lzh": "zh", "maa": "omq-maz", "mab": "omq-mxt", "maj": "omq-maz", "maq": "omq-maz", "mau": "omq-maz", "mbb": "mno", "mbd": "mno", "mbi": "mno", "mbs": "mno", "mbt": "mno", "mbz": "omq-mxt", "mce": "omq-mxt", "mdv": "omq-mxt", "meh": "omq-mxt", "meo": "ms", "mey": "ar", "mfa": "ms", "mga": "ga", "mhp": "ms", "mhr": "chm", "mib": "omq-mxt", "mid": "arc", "mie": "omq-mxt", "mig": "omq-mxt", "mih": "omq-mxt", "mii": "omq-mxt", "mil": "omq-mxt", "mim": "omq-mxt", "mio": "omq-mxt", "mip": "omq-mxt", "mis-hkl": "zh", "mis-tnw": "zh", "mit": "omq-mxt", "miu": "omq-mxt", "mix": "omq-mxt", "miy": "omq-mxt", "miz": "omq-mxt", "mjc": "omq-mxt", "mkh-mmn": "mnw", "mkh-mvi": "vi", "mkn": "ms", "mks": "omq-mxt", "mku": "man", "mkx": "mno", "mnk": "man", "mnp": "zh", "mns-cen": "mns", "mns-nor": "mns", "mns-sou": "mns", "mnw-tha": "mnw", "moe": "cr", "mpm": "omq-mxt", "mqg": "ms", "mqh": "omq-mxt", "mqk": "mno", "mrj": "chm", "mrr": "mds", "msc": "man", "msi": "ms", "msm": "mno", "mta": "mno", "mtu": "omq-mxt", "mtx": "omq-mxt", "mut": "dra-mur", "mvg": "omq-mxt", "mxa": "omq-mxt", "mxb": "omq-mxt", "mxl": "alv-gbe", "mxs": "omq-mxt", "mxt": "omq-mxt", "mxv": "omq-mxt", "mxy": "omq-mxt", "myz": "arc", "mza": "omq-mxt", "mzi": "omq-maz", "nan": "zh", "nan-dat": "zh", "nan-hbl": "zh", "nan-hlh": "zh", "nan-lnx": "zh", "nan-tws": "zh", "nan-zhe": "zh", "nan-zsh": "zh", "naz": "nah", "nb": "no", "nbw": "nic-ngd", "ncb": "aav-nic", "nch": "nah", "nci": "nah", "ncj": "nah", "ncl": "nah", "ncx": "nah", "nds-de": "nds", "nds-nl": "nds", "ngb": "nic-ngd", "ngu": "nah", "nhc": "nah", "nhe": "nah", "nhg": "nah", "nhi": "nah", "nhk": "nah", "nhm": "nah", "nhn": "nah", "nhp": "nah", "nhq": "nah", "nht": "nah", "nhv": "nah", "nhw": "nah", "nhx": "nah", "nhy": "nah", "nhz": "nah", "nik": "aav-nic", "nku": "alv-kul", "nlv": "nah", "nn": "no", "nod": "th", "nos": "tbq-nis", "npl": "nah", "nqg": "alv-ede", "nqk": "alv-ede", "nrt": "kyl", "nsd": "tbq-nis", "nsk": "cr", "nsl": "no", "nsu": "nah", "nsv": "tbq-nis", "nuz": "nah", "nwc": "new", "nwx": "new", "nzz": "qfa-dgn", "oav": "av", "obo": "mno", "obr": "my", "obt": "br", "och": "zh", "oco": "kw", "odt": "nl", "ofs": "gmw-fri", "oge": "ka", "ohu": "hu", "ojp": "ja", "okm": "ko", "oko": "ko", "okz": "km", "olt": "lt", "omp": "mni", "omq-sjq": "omq-cha", "omq-tel": "omq-mxt", "omq-teo": "omq-cha", "omr": "mr", "omx": "mnw", "ort": "or", "osn": "su", "osp": "es", "ota": "tr", "otb": "bo", "ote": "oto-otm", "otl": "oto-otm", "otm": "oto-otm", "otn": "oto-otm", "otq": "oto-otm", "ots": "oto-otm", "ott": "oto-otm", "otx": "oto-otm", "oty": "ta", "otz": "oto-otm", "owl": "cy", "pal": "fa", "pbm": "omq-maz", "peo": "fa", "pga": "ar", "pgl": "ga", "pht": "th", "pmu": "pa", "pmy": "ms", "pnt": "el", "poz-sml": "ms", "poz-ter": "ms", "pra-niy": "pra", "pro": "oc", "pse": "ms", "psh": "inc-pas", "psi": "inc-pas", "qwc": "qu", "rif": "ber", "rmc": "rom", "rmf": "rom", "rml": "rom", "rmn": "rom", "rmo": "rom", "rmu": "rom", "rmw": "rom", "rmy": "rom", "roa-oca": "ca", "roa-ole": "roa-leo", "sam": "arc", "sbd": "smq", "scs": "den", "sdc": "sc", "sdn": "sc", "se": "smi", "sel-nor": "sel", "sel-sou": "sel", "sga": "ga", "shi": "ber", "shu": "ar", "shy": "ber", "sia": "smi", "sjc": "zh", "sjd": "smi", "sje": "smi", "sjk": "smi", "sjt": "smi", "sju": "smi", "slm": "smd", "sma": "smi", "smj": "smi", "sml": "smd", "smn": "smi", "smp": "he", "sms": "smi", "sou": "th", "ssb": "smd", "ssh": "ar", "stj": "smq", "stq": "gmw-fri", "swb": "bnt-com", "swg": "de", "sxk": "kyl", "sxw": "alv-gbe", "syc": "arc", "sym": "smq", "syn": "arc", "tde": "qfa-dgn", "tfi": "alv-gbe", "the": "inc-tha", "thl": "inc-tha", "thq": "inc-tha", "thr": "inc-tha", "tji": "sit-tja", "tjs": "sit-tja", "tkt": "inc-tha", "trc": "omq-tri", "trg": "arc", "trk-oat": "tr", "trq": "omq-tri", "trs": "omq-tri", "tru": "arc", "tsd": "el", "tzm": "ber", "vkp": "pt", "vkt": "ms", "vmc": "omq-mxt", "vmd": "dra-kor", "vmf": "de", "vmj": "omq-mxt", "vmm": "omq-mxt", "vmp": "omq-maz", "vmq": "omq-mxt", "vmx": "omq-mxt", "vmy": "omq-maz", "vmz": "omq-maz", "wci": "alv-gbe", "wec": "kro-wee", "wem": "alv-gbe", "wlc": "bnt-com", "wlm": "cy", "wni": "bnt-com", "wuu": "zh", "wxa": "zh", "xaa": "ar", "xbm": "br", "xcl": "hy", "xct": "bo", "xhm": "km", "xkb": "alv-ede", "xme-mid": "xme", "xme-old": "xme", "xmm": "ms", "xpe": "kpe", "xrb": "alv-krb", "xsl": "den", "xta": "omq-mxt", "xtb": "omq-mxt", "xtd": "omq-mxt", "xti": "omq-mxt", "xtj": "omq-mxt", "xtl": "omq-mxt", "xtm": "omq-mxt", "xtn": "omq-mxt", "xtp": "omq-mxt", "xts": "omq-mxt", "xtt": "omq-mxt", "xtu": "omq-mxt", "xty": "omq-mxt", "xwe": "alv-gbe", "xwl": "alv-gbe", "xzp": "zap", "yej": "el", "yhl": "tbq-phw", "yik": "tbq-lal", "yit": "tbq-lal", "yiv": "tbq-nis", "ykg": "qfa-yuk", "yln": "qfa-buy", "ymc": "tbq-muj", "ymq": "tbq-muj", "ymx": "tbq-muj", "yok-bvy": "yok", "yok-dly": "yok", "yok-gsy": "yok", "yok-kry": "yok", "yok-nvy": "yok", "yok-ply": "yok", "yok-svy": "yok", "yok-tky": "yok", "ypb": "tbq-phw", "ypn": "tbq-phw", "yrn": "qfa-buy", "yue": "zh", "yux": "qfa-yuk", "ywl": "tbq-lal", "ywt": "tbq-lal", "yzg": "qfa-buy", "zaa": "zap", "zab": "zap", "zac": "zap", "zad": "zap", "zae": "zap", "zaf": "zap", "zai": "zap", "zam": "zap", "zao": "zap", "zaq": "zap", "zar": "zap", "zas": "zap", "zat": "zap", "zav": "zap", "zaw": "zap", "zax": "zap", "zca": "zap", "zdj": "bnt-com", "zhx-sht": "zh", "zhx-sic": "zh", "zhx-tai": "zh", "zls-chs": "cu", "zlw-ocs": "cs", "zlw-opl": "pl", "zlw-osk": "sk", "zmi": "ms", "zoo": "zap", "zpa": "zap", "zpb": "zap", "zpc": "zap", "zpd": "zap", "zpe": "zap", "zpf": "zap", "zpg": "zap", "zph": "zap", "zpi": "zap", "zpj": "zap", "zpk": "zap", "zpl": "zap", "zpm": "zap", "zpn": "zap", "zpo": "zap", "zpp": "zap", "zpq": "zap", "zpr": "zap", "zps": "zap", "zpt": "zap", "zpu": "zap", "zpv": "zap", "zpw": "zap", "zpx": "zap", "zpy": "zap", "zpz": "zap", "zsr": "zap", "zte": "zap", "ztg": "zap", "ztl": "zap", "ztm": "zap", "ztn": "zap", "ztp": "zap", "ztq": "zap", "zts": "zap", "ztt": "zap", "ztu": "zap", "ztx": "zap", "zty": "zap" }; // Kurmancî names of the families in nesting, from [[Modul:malbat/code to canonical name]]. var groupNames = { "aav-nic": "nîkobarî", "alv-bag": "baga", "alv-ede": "ede", "alv-gbe": "gbe", "alv-kis": "kissî", "alv-krb": "karaboro", "alv-kul": "kulango", "alv-lim": "lîmba", "apa": "apaçî", "ber": "berberî", "bnt-com": "komorî", "btk": "batakî", "chm": "marî", "den": "slavey", "dra-kor": "koraga", "dra-mur": "muriya", "gio": "gelaoyî", "gmw-fri": "frîsî", "inc-pas": "paşayî", "inc-tha": "tharu", "kar": "karenî", "kca": "xantî", "kro-aiz": "ayizî", "kro-bet": "bétéyî", "kro-did": "didayî", "kro-grb": "greboyî", "kro-wee": "wiyî", "mno": "manoboyî", "mns": "mansî", "nic-ngd": "nîgbandî", "omq-cha": "çatînoyî", "omq-cui": "kuyikatekî", "omq-maz": "mazatekanî", "omq-mxt": "mîkstekî", "omq-tri": "trîkiyî", "oto-otm": "otomî", "qfa-buy": "buyangî", "qfa-dgn": "dogonî", "qfa-yuk": "yukaghirî", "sel": "selkupî", "sit-bai": "bayî", "sit-kha": "khamî", "sit-tja": "tujya", "smi": "samîkî", "tbq-jin": "jîno", "tbq-lal": "laloyî", "tbq-muj": "mujî", "tbq-nis": "nîsu", "tbq-phw": "phowa", "wen": "sorbî", "xme": "medî", "yok": "yokutsanî" }; // The code of the language this one is nested under, or "". this.getNested = function(lang) { return nesting[lang] || ""; }; // The Kurmancî name of a family that languages are nested under, or undefined. this.getGroupName = function(code) { return groupNames[code]; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> ezh6ctqesr9ki7biiet7gzwj6al09ub 6794086 6794074 2026-10-14T21:26:32Z Wikihez 7344 Nesting: Guarani and Nenets groups 6794086 javascript text/javascript // <nowiki> /* * Package file of [[MediaWiki:Gadget-wikt.translation-adder.js]]: per-language data for the form. * Forked from en.wiktionary's [[en:MediaWiki:Gadget-TranslationAdder-Data.js]]. Gender codes in the * table are en.wiktionary's; getGenders() returns them as the codes of [[Modul:gender and number/data]]. */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query this information directly from [[Module:languages]]? var metadata = { aa: { haswikt: 1, }, ab: { haswikt: 1, }, aer: { }, af: { g: ["s", "p"], haswikt: 1 }, ak: { haswikt: 1 }, akk: { g: ["m", "f", "m-p", "f-p"], }, als: { haswikt: 1 }, am: { g: ["m", "f", "m-p", "f-p"], haswikt: 1, }, an: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ang: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, ar: { g: ["m", "f", "m-p", "f-p", "p"], alt: 1, haswikt: 1, }, arc: { g: ["m", "f", "m-p", "f-p"], }, are: {}, arz: { alt: 1, g: ["m", "f", "m-p", "f-p"], }, as: { haswikt: 1, }, ast: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, av: { haswikt: 1 }, ay: { haswikt: 1 }, az: { g: ["s", "p"], haswikt: 1, }, ba: { g: ["s", "p"], }, bar: {}, "bat-smg": { g: ["m", "f", "m-p", "f-p"] }, bbl: { }, be: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, "be-x-old": { }, bg: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, bh: { haswikt: 1, }, bhb: { }, bi: { haswikt: 1 }, blt: { }, bm: { haswikt: 1, }, bn: { haswikt: 1, }, bo: { haswikt: 1, }, br: { g: ["m", "f"], haswikt: 1 }, ca: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, cdo: { }, ce: { }, ch: { haswikt: 1 }, chr: { haswikt: 1, }, cjy: { }, ckb: { wiktprefix: "ku", }, cmn: { wiktprefix: "zh" }, co: { haswikt: 1 }, cpx: { }, cr: { haswikt: 1, }, crh: {}, cs: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, csb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, cu: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, cv: { }, cy: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, czh: { }, czo: { }, da: { g: ["c", "n", "c-p", "n-p", "p"], haswikt: 1 }, dax: {}, de: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, allowCaps: 1 }, dhg: {}, djb: {}, dji: {}, djr: {}, dng: { }, dsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, dsx: {}, duj: {}, dv: { g: ["s", "p"], haswikt: 1, }, dz: { haswikt: 1, }, el: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, en: { g: ["s", "p"], haswikt: 1 }, eo: { g: ["s", "p"], haswikt: 1 }, es: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, et: { g: ["s", "p"], haswikt: 1 }, ett: { g: ["s", "p"], }, eu: { g: ["s", "p"], haswikt: 1 }, fa: { haswikt: 1, g: ["s", "p"], }, // modelling on cmn, nb, prs "fa-cls": { wiktprefix: "fa", }, // modelling on cmn, nb, prs "fa-ira": { wiktprefix: "fa", }, fi: { g: ["s", "p"], haswikt: 1 }, fil: {}, fj: { haswikt: 1 }, fo: { g: ["m", "f", "n"], haswikt: 1 }, fr: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, frm: { g: ["m", "f", "m-p", "f-p"] }, fro: { g: ["m", "f", "m-p", "f-p"] }, fur: { g: ["m", "f", "m-p", "f-p"], }, fy: { g: ["c", "n", "p"], haswikt: 1 }, ga: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, gan: { }, gd: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, gez: { }, gl: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, "gmw-ecg": { g: ["m", "f", "n", "p"], allowCaps: 1 }, gmy: { }, gnn: { }, got: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], }, grc: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"] }, gsw: { g: ["m", "f", "n", "p"], allowCaps: 1 }, gu: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, guf: { }, gug: { wiktprefix: "gn" }, gv: { haswikt: 1 }, ha: { haswikt: 1 }, hak: { }, har: { }, he: { alt: 1, g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hi: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, hif: { }, hit: { }, hrx: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, hsb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, hsn: { }, hu: { g: ["s", "p"], haswikt: 1 }, hy: { g: ["s", "p"], haswikt: 1, }, ia: { haswikt: 1 }, id: { haswikt: 1 }, ie: { haswikt: 1 }, ik: { haswikt: 1 }, ike: { }, ikt: { }, io: { haswikt: 1 }, is: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, it: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, iu: { haswikt: 1, }, ja: { haswikt: 1, }, jay: { }, jbo: { haswikt: 1 }, jv: { haswikt: 1 }, ka: { g: ["s", "p"], haswikt: 1 }, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration? khb: { }, kjh: { }, kk: { haswikt: 1, g: ["s", "p"], }, kkh: { }, kl: { haswikt: 1 }, km: { haswikt: 1, }, kn: { haswikt: 1, }, kmr: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "ku" }, ko: { haswikt: 1, }, krc: { g: ["s", "p"], }, ks: { haswikt: 1, }, kw: { haswikt: 1 }, ky: { haswikt: 1, g: ["s", "p"], }, la: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, lb: { g: ["m", "f", "n", "p"], haswikt: 1, allowCaps: 1 }, lez: { }, li: { haswikt: 1 }, lki: { wiktprefix: "ku" }, lld: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lmo: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, ln: { haswikt: 1 }, lo: { haswikt: 1, }, lt: { alt: 1, g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lv: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, lzz: { }, mg: { haswikt: 1 }, mh: { haswikt: 1 }, mi: { g: 0, haswikt: 1 }, mk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ml: { haswikt: 1, }, mn: { g: ["s", "p"], haswikt: 1, }, mnp: { }, mo: { haswikt: 1, }, mol: { }, mr: { g: ["m", "f", "n"], haswikt: 1, }, ms: { haswikt: 1, }, mt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, mwp: { }, my: { haswikt: 1, }, na: { haswikt: 1 }, nah: { haswikt: 1 }, nan: { wiktprefix: "zh-min-nan", }, nb: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], wiktprefix: "no" }, nds: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, "nds-de": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, "nds-nl": { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, ne: { haswikt: 1, }, nl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nn: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, no: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1 }, nod: { }, non: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, oc: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, om: { haswikt: 1 }, or: { haswikt: 1, }, orv: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, osc: { }, ota: { g: ["s", "p"], }, pa: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pdt: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "nds" }, peo: { }, phn: { }, pi: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1 }, pjt: { }, pl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, pms: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, pox: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"] }, prs: { wiktprefix: "fa", }, ps: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, pt: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1 }, qu: { haswikt: 1 }, rgn: { g: ["m", "f", "m-p", "f-p"], }, rit: { }, rm: { g: ["m", "f"], haswikt: 1 }, rn: { haswikt: 1 }, ro: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, "roa-rup": { haswikt: 1 }, ru: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, rue: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], }, ruo: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rup: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], wiktprefix: "roa-rup" }, ruq: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, rw: { haswikt: 1 }, sa: { g: ["m", "f", "n", "m-p", "f-p", "n-p"], haswikt: 1, }, sah: { }, sc: { haswikt: 1 }, scn: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, sco: { }, sd: { haswikt: 1, }, sdh: { wiktprefix: "ku" }, sg: { haswikt: 1 }, sh: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, si: { haswikt: 1, }, simple: { haswikt: 1 }, sk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sl: { alt: 1, g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1 }, sm: { haswikt: 1 }, sn: { haswikt: 1 }, so: { haswikt: 1 }, spx: { }, sq: { g: ["m", "f"], haswikt: 1 }, ss: { haswikt: 1 }, st: { haswikt: 1 }, su: { haswikt: 1 }, sux: { }, sv: { g: ["c", "n", "c-p", "n-p"], haswikt: 1 }, sva: { }, sw: { nclass: 1, haswikt: 1 }, syc: { }, syr: { }, szl: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"] }, ta: { haswikt: 1, }, tdd: { }, te: { haswikt: 1, }, tg: { g: ["s", "p"], haswikt: 1, }, th: { haswikt: 1, }, ti: { haswikt: 1, }, tig: { }, tiw: { }, tk: { g: ["s", "p"], haswikt: 1 }, tl: { haswikt: 1, }, tmr: { }, tn: { haswikt: 1 }, to: { haswikt: 1 }, tpi: { haswikt: 1 }, tr: { g: ["s", "p"], alt: 1, haswikt: 1 }, ts: { haswikt: 1 }, tt: { g: ["s", "p"], haswikt: 1 }, tts: { }, tw: { haswikt: 1 }, udi: { }, ug: { g: ["s", "p"], haswikt: 1, }, uga: { }, uk: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"], haswikt: 1, }, ulk: { }, ur: { g: ["m", "f", "m-p", "f-p", "p"], haswikt: 1, }, uz: { g: ["s", "p"], haswikt: 1 }, vec: { g: ["m", "f", "m-p", "f-p"], haswikt: 1 }, vi: { haswikt: 1 }, vls: { g: ["m", "f", "n", "m-p", "f-p", "n-p"] }, vo: { haswikt: 1 }, wa: { haswikt: 1 }, wbp: { }, wo: { haswikt: 1 }, wuu: { }, xae: { }, xcr: { }, xfa: { }, xh: { alt: 1, nclass: 1, haswikt: 1 }, xlc: { }, xld: { }, xlu: { }, xmf: { }, xno: { g: ["m", "f", "m-p", "f-p"] }, xrr: { }, xst: { }, xum: { }, xve: { }, xvo: { }, yi: { g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"], haswikt: 1, }, yo: { haswikt: 1 }, yua: { g: ["s", "p"], alt: 1 }, yue: { }, za: { haswikt: 1, }, "zh-classical": { }, "zh-min-nan": { haswikt: 1 }, "zh-yue": { }, zu: { alt: 1, nclass: 1, haswikt: 1 } }; var clean = { aar: "aa", afar: "aa", abk: "ab", abkhazian: "ab", afr: "af", afrikaans: "af", aka: "ak", akan: "ak", amh: "am", amharic: "am", ara: "ar", arabic: "ar", arg: "an", aragonese: "an", asm: "as", assamese: "as", ava: "av", avaric: "av", ave: "ae", avestan: "ae", aym: "ay", aymara: "ay", aze: "az", azerbaijani: "az", bak: "ba", bashkir: "ba", bam: "bm", bambara: "bm", bel: "be", belarusian: "be", ben: "bn", bengali: "bn", bis: "bi", bislama: "bi", bod: "bo", tibetan: "bo", bs: "sh", bos: "sh", bosnian: "sh", bre: "br", breton: "br", bul: "bg", bulgarian: "bg", cat: "ca", catalan: "ca", ces: "cs", czech: "cs", cha: "ch", chamorro: "ch", che: "ce", chechen: "ce", chu: "cu", churchslavic: "cu", chv: "cv", chuvash: "cv", cor: "kw", cornish: "kw", cos: "co", corsican: "co", cre: "cr", cree: "cr", cym: "cy", welsh: "cy", dan: "da", danish: "da", deu: "de", german: "de", div: "dv", dhivehi: "dv", dzo: "dz", dzongkha: "dz", ekk: "et", ell: "el", greek: "el", eng: "en", english: "en", epo: "eo", esperanto: "eo", est: "et", estonian: "et", eus: "eu", basque: "eu", ewe: "ee", fao: "fo", faroese: "fo", fas: "fa", //"fa-ira": "fa", //"fa-cls": "fa", //prs: "fa", persian: "fa", fij: "fj", fijian: "fj", fil: "tl", fin: "fi", finnish: "fi", fra: "fr", french: "fr", fry: "fy", westernfrisian: "fy", ful: "ff", fulah: "ff", gla: "gd", scottishgaelic: "gd", gle: "ga", irish: "ga", glg: "gl", galician: "gl", guj: "gu", gujarati: "gu", gn: "gug", grn: "gug", guarani: "gug", glv: "gv", manx: "gv", hat: "ht", haitian: "ht", hau: "ha", hausa: "ha", heb: "he", hebrew: "he", her: "hz", herero: "hz", hin: "hi", hindi: "hi", hmo: "ho", hirimotu: "ho", hr: "sh", hrv: "sh", croatian: "sh", hun: "hu", hungarian: "hu", hye: "hy", armenian: "hy", ibo: "ig", igbo: "ig", ido: "io", iii: "ii", sichuanyi: "ii", iku: "iu", inuktitut: "iu", ile: "ie", interlingue: "ie", ina: "ia", interlingua: "ia", ind: "id", indonesian: "id", ipk: "ik", inupiaq: "ik", isl: "is", icelandic: "is", ita: "it", italian: "it", jav: "jv", javanese: "jv", jpn: "ja", japanese: "ja", kal: "kl", kalaallisut: "kl", kan: "kn", kannada: "kn", kas: "ks", kashmiri: "ks", kat: "ka", georgian: "ka", kau: "kr", kanuri: "kr", kaz: "kk", kazakh: "kk", khm: "km", centralkhmer: "km", kik: "ki", kikuyu: "ki", kin: "rw", kinyarwanda: "rw", kir: "ky", kirghiz: "ky", kom: "kv", komi: "kv", kon: "kg", kongo: "kg", kor: "ko", korean: "ko", ksh: "gmw-cfr", kua: "kj", kuanyama: "kj", kv: "kpv", lao: "lo", lat: "la", latin: "la", lav: "lv", latvian: "lv", lim: "li", limburgan: "li", lin: "ln", lingala: "ln", lit: "lt", lithuanian: "lt", ltz: "lb", luxembourgish: "lb", lub: "lu", lubakatanga: "lu", lug: "lg", ganda: "lg", mah: "mh", marshallese: "mh", mal: "ml", malayalam: "ml", mar: "mr", marathi: "mr", chm: "mhr", mari: "mhr", mkd: "mk", macedonian: "mk", mlg: "mg", malagasy: "mg", mlt: "mt", maltese: "mt", mon: "mn", mongolian: "mn", mri: "mi", maori: "mi", msa: "ms", malay: "ms", mya: "my", burmese: "my", nau: "na", nauru: "na", hokkien: "nan-hbl", hokkienese: "nan-hbl", fujian: "nan-hbl", fujianese: "nan-hbl", hainam: "hnm", hainan: "hnm", hainanese: "hnm", leizhou: "luh", "zhx-lui": "luh", teochew: "nan-tws", teochewese: "nan-tws", "zhx-teo": "nan-tws", wep: "nds", westphalian: "nds", germanlowgerman: "nds", dutchlowsaxon: "nds", act: "nds", achterhoeks: "nds", drt: "nds", drents: "nds", gos: "nds", gronings: "nds", sdz: "nds", sallands: "nds", stl: "nds", stellingwerfs: "nds", twd: "nds", twents: "nds", vel: "nds", veluws: "nds", nav: "nv", navajo: "nv", nbl: "nr", southndebele: "nr", nde: "nd", northndebele: "nd", ndo: "ng", ndonga: "ng", nep: "ne", nepali: "ne", nld: "nl", dutch: "nl", flemish: "nl", nno: "nn", norwegiannynorsk: "nn", nob: "nb", norwegianbokmal: "nb", nor: "no", norwegian: "no", nya: "ny", nyanja: "ny", oci: "oc", occitan: "oc", oji: "oj", ojibwa: "oj", ori: "or", oriya: "or", orm: "om", oromo: "om", oss: "os", ossetian: "os", pan: "pa", panjabi: "pa", pfl: "gmw-rfr", pli: "pi", pali: "pi", pol: "pl", polish: "pl", por: "pt", portuguese: "pt", pus: "ps", pushto: "ps", que: "qu", quechua: "qu", roh: "rm", romansh: "rm", ron: "ro", romanian: "ro", rn: "rw", run: "rw", rundi: "rw", rus: "ru", russian: "ru", sag: "sg", sango: "sg", san: "sa", sanskrit: "sa", sin: "si", sinhala: "si", slk: "sk", slovak: "sk", slv: "sl", slovenian: "sl", sme: "se", northernsami: "se", smo: "sm", samoan: "sm", sna: "sn", shona: "sn", snd: "sd", sindhi: "sd", som: "so", somali: "so", sot: "st", southernsotho: "st", spa: "es", spanish: "es", sqi: "sq", albanian: "sq", srd: "sc", sardinian: "sc", sr: "sh", srp: "sh", serbian: "sh", ssw: "ss", swati: "ss", sun: "su", sundanese: "su", swa: "sw", swahili: "sw", swe: "sv", swedish: "sv", tah: "ty", tahitian: "ty", tam: "ta", tamil: "ta", tat: "tt", tatar: "tt", tel: "te", telugu: "te", tgk: "tg", taishanese: "zhx-tai", tajik: "tg", tgl: "tl", tagalog: "tl", tha: "th", thai: "th", tir: "ti", tigrinya: "ti", ton: "to", tonga: "to", tsn: "tn", tswana: "tn", tso: "ts", tsonga: "ts", tuk: "tk", turkmen: "tk", tur: "tr", turkish: "tr", twi: "tw", uig: "ug", uighur: "ug", ukr: "uk", ukrainian: "uk", urd: "ur", urdu: "ur", uzb: "uz", uzbek: "uz", ven: "ve", venda: "ve", vie: "vi", vietnamese: "vi", westflemish: "vls", vol: "vo", volapuk: "vo", wln: "wa", walloon: "wa", wol: "wo", wolof: "wo", xho: "xh", xhosa: "xh", yid: "yi", yiddish: "yi", yor: "yo", yoruba: "yo", zbc: "lod", zbe: "lod", zbw: "lod", zha: "za", zhuang: "za", zh: "cmn", zho: "cmn", chinese: "cmn", zul: "zu", zulu: "zu", }; // }}} var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" }; var stripGraveAndAcute = { strip: "\u0300\u0301" }; var fullToHalfWidthNumbers = { from: "0123456789", to: "0123456789" }; // These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal. var diacriticStrippers = { ang: { from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to: "AaÆæCcEeGgIiOoUuYy", strip: "\u0304\u0307", }, //macron and above dot ar: stripArabicDiacritics, aao: stripArabicDiacritics, acm: stripArabicDiacritics, acx: stripArabicDiacritics, adf: stripArabicDiacritics, aeb: stripArabicDiacritics, afb: stripArabicDiacritics, ajp: stripArabicDiacritics, apc: stripArabicDiacritics, apd: stripArabicDiacritics, arq: stripArabicDiacritics, ary: stripArabicDiacritics, arz: stripArabicDiacritics, fa: stripArabicDiacritics, ps: stripArabicDiacritics, sd: stripArabicDiacritics, ur: stripArabicDiacritics, chl: { from: "ÁáÉéÍíÓóÚú", to: "AaEeIiOoUu", strip: "\u0304", }, //acute accent he: { strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״", }, la: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUuYy", strip: "\u0304", }, //macron lt: { from: "áãàéẽèìýỹñóõòúù", to: "aaaeeeiyynooouu", strip: "\u0340\u0301\u0303", }, nci: { from: "ĀāĒēĪīŌōŪūȲȳ", to: "AaEeIiOoUu", strip: "\u0304", }, //macron //strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list ru: stripGraveAndAcute, uk: stripGraveAndAcute, be: stripGraveAndAcute, bg: stripGraveAndAcute, orv: stripGraveAndAcute, cu: stripGraveAndAcute, rue: stripGraveAndAcute, mk: stripGraveAndAcute, sh: { from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip: "\u030F\u0300\u0311\u0301\u0304", }, sl: { from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323", }, kk: stripGraveAndAcute, ky: stripGraveAndAcute, tg: stripGraveAndAcute, sa: { strip: "ः", }, /** visarga **/ bo: { strip: "།", }, /** shad **/ tr: { from: "ÂâÛû", to: "AaUu", strip: "\u0302", }, ja: fullToHalfWidthNumbers, cmn: fullToHalfWidthNumbers, yue: fullToHalfWidthNumbers, nan: fullToHalfWidthNumbers, ko: fullToHalfWidthNumbers, zu: { strip_init_hyphen: true, } }; function stringOrNull(val) { return typeof val == "string" ? val : null; } function makeMap(obj) { var from = stringOrNull(obj.from); var to = stringOrNull(obj.to); var strip = stringOrNull(obj.strip); var map = {}; if (from && to) { for (var i = 0; i < from.length; i++) { map[from.charAt(i)] = to.charAt(i); } } if (strip) { for (var ii = 0; ii < strip.length; ii++) { map[strip.charAt(ii)] = ""; } } return map; } /** letters and number 1 are used instead of palochka **/ var palochkaCorrections = { from: "Il1", to: "ӏӏӏ" }; // var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH) var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF) var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH) var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH) var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA) /** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/ var persianCorrections = { from: arabicKaf + arabicYa + alifMaqsura, to: farsiKaf + farsiYe + farsiYe, }; // These replacements will be applied when a word is submitted. var orthographicalCorrections = { // Replace grave accents with acute accents for translations // into Bulgarian per Wiktionary policy bg: { map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" }, }, abq: palochkaCorrections, ady: palochkaCorrections, av: palochkaCorrections, ce: palochkaCorrections, dar: palochkaCorrections, inh: palochkaCorrections, kbd: palochkaCorrections, lbe: palochkaCorrections, lez: palochkaCorrections, tab: palochkaCorrections, /** Roman to Cyrillic**/ cv: { from: "ĂăĔĕÇçŸÿ", to: "ӐӑӖӗҪҫӲӳ", }, kv: { from: "ÖöIi", to: "ӦӧІі", }, koi: { from: "ÖöIi", to: "ӦӧІі", }, kpv: { from: "ÖöIi", to: "ӦӧІі", }, os: { from: "Ææ", to: "Ӕӕ", }, /* Obsolete or incorrectly used letters */ mn: { from: "ЄєѲѳЇїVv", to: "ӨөӨөҮүҮү", strip: "\u0300\u0301", }, /* cedilla to comma below */ ro: { from: "ŞŢşţ", to: "ȘȚșț", }, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ uz: { from: "'`", to: "ʻʻ", }, fa: persianCorrections, "fa-ira": persianCorrections, "fa-cls": persianCorrections, prs: persianCorrections, ur: persianCorrections, /** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/ ar: { from: farsiKaf + farsiYe, to: arabicKaf + arabicYa, }, ota: { from: farsiKaf + arabicYa + alifMaqsura, to: arabicKaf + farsiYe + farsiYe, }, ps: { from: arabicKaf, to: farsiKaf, }, /** some letters are considered more standard (to be expanded) **/ cu: { from: "ыѹ", to: "ꙑу", }, /** some letters are considered more standard (to be expanded) **/ orv: { from: "ыѹ", to: "ꙑу", }, /** obsolete letters **/ ab: { from: "ҔҕҦҧ", to: "ӶӷԤԥ", }, }; // Whether the language is one translations are often given in (it has an entry in the table above). this.isCommon = function(lang) { return Object.prototype.hasOwnProperty.call(metadata, lang); }; //Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) { if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }; //Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) { if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }; // Keep this in sync with ignore_caps in [[Modul:translations]]. this.ignoreCaps = { "ko": true }; // Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function(lang, title, callback) { if (this.hasWiktionary(lang)) { if (this.ignoreCaps[lang]) title = title.replace(/^\^/, ""); // Use this when we want to give up support for Internet Explorer 11. // URL constructor and URLSearchParams are not supported in IE11. // var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); // url.search = new URLSearchParams({ // "format": "json", // "formatversion": "2", // "action": "query", // "titles": title, // "converttitles": 1, // "origin": "*", // }); var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php"); url.searchParams.set("format", "json"); url.searchParams.set("formatversion", "2"); url.searchParams.set("action", "query"); url.searchParams.set("titles", title); url.searchParams.set("converttitles", "1"); url.searchParams.set("origin", "*"); $.ajax({ type: 'GET', url: url.toString(), dataType: 'json', headers: { 'Api-User-Agent': 'KuWiktTranslationAdderGadget/0.1 (https://ku.wiktionary.org/wiki/MediaWiki:Gadget-wikt.translation-adder.js)' }, }).done(function(data) { var page = data && data.query && data.query.pages && data.query.pages[0]; callback(page instanceof Object ? !page.missing : false); }).fail(function() { // Unknown: the line gets {{W}}, without the interwiki link. callback(false); }); } else callback(false); }; //Given a language code return a default script code. this.guessScript = function(lang) { var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang); if (scripts && scripts.length > 0) return scripts[0]; else return false; }; // In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function(lang, title, word) { if (metadata[lang] && metadata[lang].allowCaps) return true; if (title.substr(0, 1).toLowerCase() != title.substr(0, 1)) return true; return word.substr(0, 1).toLowerCase() == word.substr(0, 1); }; // en.wiktionary gender/number code -> ku.wiktionary code. var kuGender = { m: "n", f: "m", n: "nt", c: "g", s: "y", d: "d", p: "p", impf: "impf", pf: "pf" }; // The language's gender/number codes as ku.wiktionary writes them, or undefined. this.getGenders = function(lang) { if (metadata[lang] && metadata[lang].g) return metadata[lang].g.map(function (code) { return code.split("-").map(function (part) { return kuGender[part] || part; }).join("-"); }); }; //Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) { if (metadata[lang]) return metadata[lang].nclass; }; //Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) { if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }; function applyMap(map, str) { var input = str.split(""); var output = ""; for (var i = 0; i < input.length; i++) { var char = input[i]; var repl = map[char]; output += repl ? repl : char; } return output; } this.applyOrthographicalCorrections = function(lang, word) { word = word.replace('\xAD', ''); // remove soft hyphens var corrections = orthographicalCorrections[lang]; if (corrections) { var map = corrections.map ? corrections.map : makeMap(corrections); return applyMap(map, word); } return word; }; // Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function(lang, word) { var stripper = diacriticStrippers[lang]; if (stripper) { var map = makeMap(stripper); var output = applyMap(map, word); if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-') output = output.substr(1); return output; } }; // Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page // name computed from rawPageName is different from the one that would have been // computed from just word). this.retrieveRawPageName = function(lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function(c) { return '{{' + temps[c] + '}}'; }); s = s.replace(/[<&>]/g, function(c) { return '&' + ents[c] + ';'; }); s = s.replace(/\t/g, ' '); //s = encodeURIComponent(s); return s; } function removeDiacritics(s) { return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}'; } var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word); $.ajax({ dataType: "json", url: '/w/api.php', data: { format: 'json', action: 'expandtemplates', prop: 'wikitext', text: text }, success: function(data) { data = data && data.expandtemplates && data.expandtemplates.wikitext; if (/<!--/.test(data)) { data = false; } if (data) { data = data && data.split('\t'); var wordMinus = data[0]; var rawPageNameMinus = data[1]; callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus); } else { callback(rawPageName || word, rawPageName && rawPageName != word); } }, error: function() { callback(rawPageName || word, rawPageName && rawPageName != word); } }); }; //Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) { var key = lang.toLowerCase().replace(' ', ''); if (clean[key]) return clean[key]; else return lang; }; // Language -> the language or family its line goes under (* {{Z|zh}}: / *: {{Z|cmn}}: …), from en.wiktionary's // nesting table with its group names turned into codes: a language's, else a family's from [[Modul:malbat]]. // Groups with neither are left out. Apache (apa), Berber (ber), Sardinian (sc), // Guarani (gn) and Nenets (yrk) are as ku pages write them. var nesting = { "abh": "ar", "abv": "ar", "acm": "ar", "acw": "ar", "acx": "ar", "acy": "ar", "adf": "ar", "aeb": "ar", "aee": "inc-pas", "afb": "ar", "ahi": "kro-aiz", "ahm": "kro-aiz", "ahp": "kro-aiz", "aii": "arc", "aij": "arc", "ajp": "ar", "akb": "btk", "amw": "arc", "aou": "gio", "apc": "ar", "apd": "ar", "apj": "apa", "apk": "apa", "apl": "apa", "apm": "apa", "apw": "apa", "aqd": "qfa-dgn", "arq": "ar", "ars": "ar", "ary": "ar", "arz": "ar", "atd": "mno", "atj": "cr", "auz": "ar", "axm": "hy", "ayl": "ar", "ayn": "ar", "ayp": "ar", "azd": "nah", "azn": "nah", "azz": "nah", "bar": "de", "bbc": "btk", "bca": "sit-bai", "bet": "kro-bet", "bev": "kro-bet", "bfc": "sit-bai", "bfs": "sit-bai", "bgo": "alv-bag", "bgx": "tr", "bhn": "arc", "bhv": "pdu", "bjf": "arc", "blk": "kar", "bmd": "alv-bag", "bpn": "zh", "bpq": "ms", "bpy": "mni", "bqf": "alv-bag", "bsp": "alv-bag", "bsv": "alv-bag", "btd": "btk", "btg": "kro-bet", "btj": "ms", "btm": "btk", "bts": "btk", "btx": "btk", "btz": "btk", "bve": "ms", "bvu": "ms", "bwe": "kar", "bya": "btk", "caq": "aav-nic", "cbj": "alv-ede", "cdo": "zh", "cib": "alv-gbe", "cjy": "zh", "cld": "arc", "cly": "omq-cha", "cmg": "mn", "cmn": "zh", "cnp": "zh", "cnx": "kw", "cpg": "el", "cpx": "zh", "crj": "cr", "crk": "cr", "crl": "cr", "crm": "cr", "crp-mpp": "pt", "csp": "zh", "csw": "cr", "cta": "omq-cha", "ctp": "omq-cha", "ctz": "omq-cha", "cut": "omq-cui", "cux": "omq-cui", "cwd": "cr", "cya": "omq-cha", "czh": "zh", "czn": "omq-cha", "czo": "zh", "daq": "mds", "dbg": "qfa-dgn", "dbt": "qfa-dgn", "dbu": "qfa-dgn", "dbw": "qfa-dgn", "dds": "qfa-dgn", "dgb": "qfa-dgn", "dib": "din", "dic": "kro-did", "dik": "din", "dip": "din", "diw": "din", "djm": "qfa-dgn", "dks": "din", "dmb": "qfa-dgn", "dng": "zh", "doc": "kdx", "dra-mkn": "kn", "dra-okn": "kn", "dra-ote": "te", "dsb": "wen", "dti": "qfa-dgn", "dtk": "qfa-dgn", "dtm": "qfa-dgn", "dto": "qfa-dgn", "dts": "qfa-dgn", "dtt": "qfa-dgn", "dtu": "qfa-dgn", "dum": "nl", "dym": "qfa-dgn", "egx-dem": "egy", "emu": "dra-mur", "enc": "qfa-buy", "fal": "fli", "fll": "fli", "fmu": "dra-mur", "frm": "fr", "fro": "fr", "frr": "gmw-fri", "fy": "gmw-fri", "gan": "zh", "gbh": "alv-gbe", "gbo": "kro-grb", "gbs": "alv-gbe", "gbx": "alv-gbe", "gec": "kro-grb", "ggn": "gvr", "ghk": "kar", "gie": "kro-did", "giq": "gio", "gir": "gio", "giu": "gio", "giw": "gio", "gkp": "kpe", "glh": "inc-pas", "gmh": "de", "gml": "nds", "gmq-mno": "no", "gmq-oda": "da", "gmq-ogt": "gmq-gut", "gmq-osw": "sv", "gmw-cfr": "de", "gmw-ecg": "de", "gmw-jdt": "nl", "gmw-msc": "sco", "gmw-rfr": "de", "gmy": "el", "gn-cls": "gn", "gnw": "gn", "goh": "de", "gqu": "gio", "grc": "el", "grj": "kro-grb", "grk-cal": "el", "grk-ita": "el", "grk-mar": "el", "grv": "kro-grb", "gry": "kro-grb", "gsw": "de", "gud": "kro-did", "gug": "gn", "gui": "gn", "gun": "gn", "gxx": "kro-wee", "hak": "zh", "hax": "hai", "hdn": "hai", "hnm": "zh", "hrt": "arc", "hsb": "wen", "hsn": "zh", "huy": "arc", "ica": "alv-ede", "idd": "alv-ede", "ife": "alv-ede", "ijj": "alv-ede", "ike": "iu", "inc-ash": "pra", "inc-kam": "pra", "inc-mas": "as", "inc-mbn": "bn", "inc-mgu": "gu", "inc-mor": "or", "inc-oas": "as", "inc-oaw": "awa", "inc-obn": "bn", "inc-ogu": "gu", "inc-ohi": "hi", "inc-oor": "or", "inc-opa": "pa", "jas": "jv", "jax": "ms", "jiu": "tbq-jin", "jiy": "tbq-jin", "jkp": "kar", "jmx": "omq-mxt", "jvn": "jv", "kab": "ber", "kaw": "jv", "kca-eas": "kca", "kca-nor": "kca", "kca-sou": "kca", "kfd": "dra-kor", "kgj": "sit-kha", "kif": "sit-kha", "kip": "sit-kha", "kjl": "sit-kha", "klg": "kqe", "kll": "kqe", "kmc": "kdx", "kmz": "tr", "ko-ear": "ko", "kqd": "arc", "kqk": "alv-gbe", "kqs": "alv-kis", "kss": "alv-kis", "ksw": "kar", "kvl": "kar", "kvq": "kar", "kvt": "kar", "kvu": "kar", "kvy": "kar", "kxd": "ms", "kxf": "kar", "kxk": "kar", "kxm": "km", "kza": "alv-krb", "kzc": "alv-kul", "lay": "sit-bai", "lhs": "arc", "lia": "alv-lim", "lma": "alv-lim", "lrt": "ms", "lsd": "arc", "ltc": "zh", "luh": "zh", "lzh": "zh", "maa": "omq-maz", "mab": "omq-mxt", "maj": "omq-maz", "maq": "omq-maz", "mau": "omq-maz", "mbb": "mno", "mbd": "mno", "mbi": "mno", "mbs": "mno", "mbt": "mno", "mbz": "omq-mxt", "mce": "omq-mxt", "mdv": "omq-mxt", "meh": "omq-mxt", "meo": "ms", "mey": "ar", "mfa": "ms", "mga": "ga", "mhp": "ms", "mhr": "chm", "mib": "omq-mxt", "mid": "arc", "mie": "omq-mxt", "mig": "omq-mxt", "mih": "omq-mxt", "mii": "omq-mxt", "mil": "omq-mxt", "mim": "omq-mxt", "mio": "omq-mxt", "mip": "omq-mxt", "mis-hkl": "zh", "mis-tnw": "zh", "mit": "omq-mxt", "miu": "omq-mxt", "mix": "omq-mxt", "miy": "omq-mxt", "miz": "omq-mxt", "mjc": "omq-mxt", "mkh-mmn": "mnw", "mkh-mvi": "vi", "mkn": "ms", "mks": "omq-mxt", "mku": "man", "mkx": "mno", "mnk": "man", "mnp": "zh", "mns-cen": "mns", "mns-nor": "mns", "mns-sou": "mns", "mnw-tha": "mnw", "moe": "cr", "mpm": "omq-mxt", "mqg": "ms", "mqh": "omq-mxt", "mqk": "mno", "mrj": "chm", "mrr": "mds", "msc": "man", "msi": "ms", "msm": "mno", "mta": "mno", "mtu": "omq-mxt", "mtx": "omq-mxt", "mut": "dra-mur", "mvg": "omq-mxt", "mxa": "omq-mxt", "mxb": "omq-mxt", "mxl": "alv-gbe", "mxs": "omq-mxt", "mxt": "omq-mxt", "mxv": "omq-mxt", "mxy": "omq-mxt", "myz": "arc", "mza": "omq-mxt", "mzi": "omq-maz", "nan": "zh", "nan-dat": "zh", "nan-hbl": "zh", "nan-hlh": "zh", "nan-lnx": "zh", "nan-tws": "zh", "nan-zhe": "zh", "nan-zsh": "zh", "naz": "nah", "nb": "no", "nbw": "nic-ngd", "ncb": "aav-nic", "nch": "nah", "nci": "nah", "ncj": "nah", "ncl": "nah", "ncx": "nah", "nds-de": "nds", "nds-nl": "nds", "ngb": "nic-ngd", "ngu": "nah", "nhc": "nah", "nhe": "nah", "nhg": "nah", "nhi": "nah", "nhk": "nah", "nhm": "nah", "nhn": "nah", "nhp": "nah", "nhq": "nah", "nht": "nah", "nhv": "nah", "nhw": "nah", "nhx": "nah", "nhy": "nah", "nhz": "nah", "nik": "aav-nic", "nku": "alv-kul", "nlv": "nah", "nn": "no", "nod": "th", "nos": "tbq-nis", "npl": "nah", "nqg": "alv-ede", "nqk": "alv-ede", "nrt": "kyl", "nsd": "tbq-nis", "nsk": "cr", "nsl": "no", "nsu": "nah", "nsv": "tbq-nis", "nuz": "nah", "nwc": "new", "nwx": "new", "nzz": "qfa-dgn", "oav": "av", "obo": "mno", "obr": "my", "obt": "br", "och": "zh", "oco": "kw", "odt": "nl", "ofs": "gmw-fri", "oge": "ka", "ohu": "hu", "ojp": "ja", "okm": "ko", "oko": "ko", "okz": "km", "olt": "lt", "omp": "mni", "omq-sjq": "omq-cha", "omq-tel": "omq-mxt", "omq-teo": "omq-cha", "omr": "mr", "omx": "mnw", "ort": "or", "osn": "su", "osp": "es", "ota": "tr", "otb": "bo", "ote": "oto-otm", "otl": "oto-otm", "otm": "oto-otm", "otn": "oto-otm", "otq": "oto-otm", "ots": "oto-otm", "ott": "oto-otm", "otx": "oto-otm", "oty": "ta", "otz": "oto-otm", "owl": "cy", "pal": "fa", "pbm": "omq-maz", "peo": "fa", "pga": "ar", "pgl": "ga", "pht": "th", "pmu": "pa", "pmy": "ms", "pnt": "el", "poz-sml": "ms", "poz-ter": "ms", "pra-niy": "pra", "pro": "oc", "pse": "ms", "psh": "inc-pas", "psi": "inc-pas", "qwc": "qu", "rif": "ber", "rmc": "rom", "rmf": "rom", "rml": "rom", "rmn": "rom", "rmo": "rom", "rmu": "rom", "rmw": "rom", "rmy": "rom", "roa-oca": "ca", "roa-ole": "roa-leo", "sam": "arc", "sbd": "smq", "scs": "den", "sdc": "sc", "sdn": "sc", "se": "smi", "sel-nor": "sel", "sel-sou": "sel", "sga": "ga", "shi": "ber", "shu": "ar", "shy": "ber", "sia": "smi", "sjc": "zh", "sjd": "smi", "sje": "smi", "sjk": "smi", "sjt": "smi", "sju": "smi", "slm": "smd", "sma": "smi", "smj": "smi", "sml": "smd", "smn": "smi", "smp": "he", "sms": "smi", "sou": "th", "ssb": "smd", "ssh": "ar", "stj": "smq", "stq": "gmw-fri", "swb": "bnt-com", "swg": "de", "sxk": "kyl", "sxw": "alv-gbe", "syc": "arc", "syd-fne": "yrk", "sym": "smq", "syn": "arc", "tde": "qfa-dgn", "tfi": "alv-gbe", "the": "inc-tha", "thl": "inc-tha", "thq": "inc-tha", "thr": "inc-tha", "tji": "sit-tja", "tjs": "sit-tja", "tkt": "inc-tha", "trc": "omq-tri", "trg": "arc", "trk-oat": "tr", "trq": "omq-tri", "trs": "omq-tri", "tru": "arc", "tsd": "el", "tzm": "ber", "vkp": "pt", "vkt": "ms", "vmc": "omq-mxt", "vmd": "dra-kor", "vmf": "de", "vmj": "omq-mxt", "vmm": "omq-mxt", "vmp": "omq-maz", "vmq": "omq-mxt", "vmx": "omq-mxt", "vmy": "omq-maz", "vmz": "omq-maz", "wci": "alv-gbe", "wec": "kro-wee", "wem": "alv-gbe", "wlc": "bnt-com", "wlm": "cy", "wni": "bnt-com", "wuu": "zh", "wxa": "zh", "xaa": "ar", "xbm": "br", "xcl": "hy", "xct": "bo", "xhm": "km", "xkb": "alv-ede", "xme-mid": "xme", "xme-old": "xme", "xmm": "ms", "xpe": "kpe", "xrb": "alv-krb", "xsl": "den", "xta": "omq-mxt", "xtb": "omq-mxt", "xtd": "omq-mxt", "xti": "omq-mxt", "xtj": "omq-mxt", "xtl": "omq-mxt", "xtm": "omq-mxt", "xtn": "omq-mxt", "xtp": "omq-mxt", "xts": "omq-mxt", "xtt": "omq-mxt", "xtu": "omq-mxt", "xty": "omq-mxt", "xwe": "alv-gbe", "xwl": "alv-gbe", "xzp": "zap", "yej": "el", "yhl": "tbq-phw", "yik": "tbq-lal", "yit": "tbq-lal", "yiv": "tbq-nis", "ykg": "qfa-yuk", "yln": "qfa-buy", "ymc": "tbq-muj", "ymq": "tbq-muj", "ymx": "tbq-muj", "yok-bvy": "yok", "yok-dly": "yok", "yok-gsy": "yok", "yok-kry": "yok", "yok-nvy": "yok", "yok-ply": "yok", "yok-svy": "yok", "yok-tky": "yok", "ypb": "tbq-phw", "ypn": "tbq-phw", "yrn": "qfa-buy", "yue": "zh", "yux": "qfa-yuk", "ywl": "tbq-lal", "ywt": "tbq-lal", "yzg": "qfa-buy", "zaa": "zap", "zab": "zap", "zac": "zap", "zad": "zap", "zae": "zap", "zaf": "zap", "zai": "zap", "zam": "zap", "zao": "zap", "zaq": "zap", "zar": "zap", "zas": "zap", "zat": "zap", "zav": "zap", "zaw": "zap", "zax": "zap", "zca": "zap", "zdj": "bnt-com", "zhx-sht": "zh", "zhx-sic": "zh", "zhx-tai": "zh", "zls-chs": "cu", "zlw-ocs": "cs", "zlw-opl": "pl", "zlw-osk": "sk", "zmi": "ms", "zoo": "zap", "zpa": "zap", "zpb": "zap", "zpc": "zap", "zpd": "zap", "zpe": "zap", "zpf": "zap", "zpg": "zap", "zph": "zap", "zpi": "zap", "zpj": "zap", "zpk": "zap", "zpl": "zap", "zpm": "zap", "zpn": "zap", "zpo": "zap", "zpp": "zap", "zpq": "zap", "zpr": "zap", "zps": "zap", "zpt": "zap", "zpu": "zap", "zpv": "zap", "zpw": "zap", "zpx": "zap", "zpy": "zap", "zpz": "zap", "zsr": "zap", "zte": "zap", "ztg": "zap", "ztl": "zap", "ztm": "zap", "ztn": "zap", "ztp": "zap", "ztq": "zap", "zts": "zap", "ztt": "zap", "ztu": "zap", "ztx": "zap", "zty": "zap" }; // Kurmancî names of the families in nesting, from [[Modul:malbat/code to canonical name]]. var groupNames = { "aav-nic": "nîkobarî", "alv-bag": "baga", "alv-ede": "ede", "alv-gbe": "gbe", "alv-kis": "kissî", "alv-krb": "karaboro", "alv-kul": "kulango", "alv-lim": "lîmba", "apa": "apaçî", "ber": "berberî", "bnt-com": "komorî", "btk": "batakî", "chm": "marî", "den": "slavey", "dra-kor": "koraga", "dra-mur": "muriya", "gio": "gelaoyî", "gmw-fri": "frîsî", "inc-pas": "paşayî", "inc-tha": "tharu", "kar": "karenî", "kca": "xantî", "kro-aiz": "ayizî", "kro-bet": "bétéyî", "kro-did": "didayî", "kro-grb": "greboyî", "kro-wee": "wiyî", "mno": "manoboyî", "mns": "mansî", "nic-ngd": "nîgbandî", "omq-cha": "çatînoyî", "omq-cui": "kuyikatekî", "omq-maz": "mazatekanî", "omq-mxt": "mîkstekî", "omq-tri": "trîkiyî", "oto-otm": "otomî", "qfa-buy": "buyangî", "qfa-dgn": "dogonî", "qfa-yuk": "yukaghirî", "sel": "selkupî", "sit-bai": "bayî", "sit-kha": "khamî", "sit-tja": "tujya", "smi": "samîkî", "tbq-jin": "jîno", "tbq-lal": "laloyî", "tbq-muj": "mujî", "tbq-nis": "nîsu", "tbq-phw": "phowa", "wen": "sorbî", "xme": "medî", "yok": "yokutsanî" }; // The code of the language this one is nested under, or "". this.getNested = function(lang) { return nesting[lang] || ""; }; // The Kurmancî name of a family that languages are nested under, or undefined. this.getGroupName = function(code) { return groupNames[code]; }; // For enforcing an ordering on nested languages. this.nestsBefore = function(a, b) { return window.wikt.translationAdder.compareNames(a, b) < 0; }; this.getScripts = function(lang) { var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || []; return scripts; }; }; //</nowiki> 58zasais7kgq9ugdgvequ1yhlpf8kr4 Bikarhêner:Wikihez/Gadget-test/werger 2 1359441 6793971 2026-10-14T18:20:43Z Wikihez 7344 Test page for the translation adder 6793971 wikitext text/x-wiki Test page for [[MediaWiki:Gadget-wikt.translation-adder.js]] (via loader.js). Lists copied from [[mişk]]. ==== Werger ==== {{werger-ser/ceribandin|heywanê biçûk ê ku [[penîr]] dixwe}} * {{Z|ab}}: {{W-|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+|af|muis}} * {{Z|sq}}: {{W+|sq|mi|n}} * {{Z|de}}: {{W+|de|Maus|m}} *: {{Z|gsw}}: {{W-|gsw|Muus|m}} * {{Z|am}}: {{W-|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-|an|zorz}} * {{Z|rup}}: {{W-|rup|shoaric}} * {{Z|aer}}: {{W-|aer|thakwere}} * {{Z|as}}: {{W-|as|নিগনি}} * {{Z|ast}}: {{W+|ast|ratu|n}}, {{W+|ast|ratón|n}}, {{W+|ast|mur}} * {{Z|tay}}: {{W-|tay|olit}} * {{Z|av}}: {{W-|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+|ay|achaku}} * {{Z|ain}}: {{W-|ain|エㇾㇺ|tr=erm}}, {{W-|ain|エㇾム|tr=ermu}}, {{W-|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+|az|siçan}} * {{Z|ban}}: {{W-|ban|bikul}}, {{W-|ban|jero ketut}} * {{Z|eu}}: {{W+|eu|sagu}} * {{Z|ba}}: {{W-|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-|be|мыш|m}}, {{W-|be|мы́шка|m}} * {{Z|bal}}: {{W-|bal|مشک|tr=mušk}}, {{W-|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+|br|logodenn|m}} * {{Z|bg}}: {{W+|bg|ми́шка|m}}, {{W-|bg|мишо́к|n}} * {{Z|my}}: {{W+|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-|pcc|ranz}}, {{W-|pcc|waauc}} * {{Z|ccc}}: {{W-|ccc|kijsili}} * {{Z|ce}}: {{W-|ce|дахка}} * {{Z|cs}}: {{W+|cs|myš|m}}, {{W+|cs|myšák|n}} * {{Z|chr}}: {{W-|chr|ᏥᏍᏕᏥ}} * {{Z|ny}}: {{W-|ny|mbewa}} * {{Z|cic}}: {{W-|cic|pinti'}} * {{Z|zh}}: *: {{Z|yue}}: {{W-|yue|老鼠|tr=lou5 syu2}} *: {{Z|dng}}: {{W-|dng|лочў}} *: {{Z|hak}}: {{W-|hak|老鼠|tr=lo-chhú, ló-chhú, no-chhú}} *: {{Z|lzh}}: {{W-|lzh|鼠|tr=shǔ}}, {{W-|lzh|鼷|tr=xī}} *: {{Z|cmn}}: {{W+|cmn|老鼠|tr=lǎoshǔ}}, {{W+|cmn|鼠|tr=shǔ}}, {{W+|cmn|耗子|tr=hàozi}}, {{W+|cmn|小鼠|tr=xiǎoshǔ}}, {{W+|cmn|鼷|tr=xī}} *: {{Z|cdo}}: {{W-|cdo|老鼠|tr=lō̤-chṳ̄}} *: {{Z|nan}}: {{W+|nan|鳥鼠|tr=niáu-chhí, niáu-chhú}}, {{W+|nan|鼠|tr=chhí, chhú}}, {{W+|nan|錢鼠|tr=chîⁿ-chhí, chîⁿ-chhú}}, {{W-|nan|老鼠|tr=ló͘-chhír, ló͘-chhí, niáu-chhú, niáu-chhí, niáu-chhír}} *: {{Z|wuu}}: {{W-|wuu|老鼠}} * {{Z|ckt}}: {{W-|ckt|пипиӄыԓгын|tr=pipiqyḷgyn}} * {{Z|cv}}: {{W-|cv|шӑши|sc=Cyrl}} * {{Z|dlm}}: {{W-|dlm|surco|n}} * {{Z|da}}: {{W+|da|mus|g}} * {{Z|dv}}: {{W-|dv|މީދާ|sc=Thaa}} * {{Z|id}}: {{W+|id|tikus}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin|amûra [[komputer]]ê|id=amûr}} * {{Z|de}}: {{W+|de|Maus|m}} * {{Z|tr}}: {{W+|tr|fare}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin}} {{werger-bin/ceribandin}} pv1386vz91vrnfijmmlsq63gu0lne6i 6794017 6793971 2026-10-14T18:51:08Z Wikihez 7344 +erebî: [[test]] +erebî: [[test2]] 6794017 wikitext text/x-wiki Test page for [[MediaWiki:Gadget-wikt.translation-adder.js]] (via loader.js). Lists copied from [[mişk]]. ==== Werger ==== {{werger-ser/ceribandin|heywanê biçûk ê ku [[penîr]] dixwe}} * {{Z|ab}}: {{W-|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+|af|muis}} * {{Z|sq}}: {{W+|sq|mi|n}} * {{Z|de}}: {{W+|de|Maus|m}} *: {{Z|gsw}}: {{W-|gsw|Muus|m}} * {{Z|am}}: {{W-|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-|an|zorz}} * {{Z|rup}}: {{W-|rup|shoaric}} * {{Z|aer}}: {{W-|aer|thakwere}} * {{Z|as}}: {{W-|as|নিগনি}} * {{Z|ast}}: {{W+|ast|ratu|n}}, {{W+|ast|ratón|n}}, {{W+|ast|mur}} * {{Z|tay}}: {{W-|tay|olit}} * {{Z|av}}: {{W-|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+|ay|achaku}} * {{Z|ain}}: {{W-|ain|エㇾㇺ|tr=erm}}, {{W-|ain|エㇾム|tr=ermu}}, {{W-|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+|az|siçan}} * {{Z|ban}}: {{W-|ban|bikul}}, {{W-|ban|jero ketut}} * {{Z|eu}}: {{W+|eu|sagu}} * {{Z|ba}}: {{W-|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-|be|мыш|m}}, {{W-|be|мы́шка|m}} * {{Z|bal}}: {{W-|bal|مشک|tr=mušk}}, {{W-|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+|br|logodenn|m}} * {{Z|bg}}: {{W+|bg|ми́шка|m}}, {{W-|bg|мишо́к|n}} * {{Z|my}}: {{W+|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-|pcc|ranz}}, {{W-|pcc|waauc}} * {{Z|ccc}}: {{W-|ccc|kijsili}} * {{Z|ce}}: {{W-|ce|дахка}} * {{Z|cs}}: {{W+|cs|myš|m}}, {{W+|cs|myšák|n}} * {{Z|chr}}: {{W-|chr|ᏥᏍᏕᏥ}} * {{Z|ny}}: {{W-|ny|mbewa}} * {{Z|cic}}: {{W-|cic|pinti'}} * {{Z|zh}}: *: {{Z|yue}}: {{W-|yue|老鼠|tr=lou5 syu2}} *: {{Z|dng}}: {{W-|dng|лочў}} *: {{Z|hak}}: {{W-|hak|老鼠|tr=lo-chhú, ló-chhú, no-chhú}} *: {{Z|lzh}}: {{W-|lzh|鼠|tr=shǔ}}, {{W-|lzh|鼷|tr=xī}} *: {{Z|cmn}}: {{W+|cmn|老鼠|tr=lǎoshǔ}}, {{W+|cmn|鼠|tr=shǔ}}, {{W+|cmn|耗子|tr=hàozi}}, {{W+|cmn|小鼠|tr=xiǎoshǔ}}, {{W+|cmn|鼷|tr=xī}} *: {{Z|cdo}}: {{W-|cdo|老鼠|tr=lō̤-chṳ̄}} *: {{Z|nan}}: {{W+|nan|鳥鼠|tr=niáu-chhí, niáu-chhú}}, {{W+|nan|鼠|tr=chhí, chhú}}, {{W+|nan|錢鼠|tr=chîⁿ-chhí, chîⁿ-chhú}}, {{W-|nan|老鼠|tr=ló͘-chhír, ló͘-chhí, niáu-chhú, niáu-chhí, niáu-chhír}} *: {{Z|wuu}}: {{W-|wuu|老鼠}} * {{Z|ckt}}: {{W-|ckt|пипиӄыԓгын|tr=pipiqyḷgyn}} * {{Z|cv}}: {{W-|cv|шӑши|sc=Cyrl}} * {{Z|dlm}}: {{W-|dlm|surco|n}} * {{Z|da}}: {{W+|da|mus|g}} * {{Z|dv}}: {{W-|dv|މީދާ|sc=Thaa}} * {{Z|id}}: {{W+|id|tikus}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin|amûra [[komputer]]ê|id=amûr}} * {{Z|de}}: {{W+|de|Maus|m}} * {{Z|ar}}: {{W+|ar|test|m}}, {{W-|ar|test2}} * {{Z|tr}}: {{W+|tr|fare}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin}} {{werger-bin/ceribandin}} omjot5gdyz8znrtiqnyyqqxtwkvt88q 6794029 6794017 2026-10-14T19:03:41Z Wikihez 7344 Use the W test copies 6794029 wikitext text/x-wiki Test page for [[MediaWiki:Gadget-wikt.translation-adder.js]] (via loader.js). Lists copied from [[mişk]]. ==== Werger ==== {{werger-ser/ceribandin|heywanê biçûk ê ku [[penîr]] dixwe}} * {{Z|ab}}: {{W-/ceribandin|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+/ceribandin|af|muis}} * {{Z|sq}}: {{W+/ceribandin|sq|mi|n}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} *: {{Z|gsw}}: {{W-/ceribandin|gsw|Muus|m}} * {{Z|am}}: {{W-/ceribandin|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-/ceribandin|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-/ceribandin|an|zorz}} * {{Z|rup}}: {{W-/ceribandin|rup|shoaric}} * {{Z|aer}}: {{W-/ceribandin|aer|thakwere}} * {{Z|as}}: {{W-/ceribandin|as|নিগনি}} * {{Z|ast}}: {{W+/ceribandin|ast|ratu|n}}, {{W+/ceribandin|ast|ratón|n}}, {{W+/ceribandin|ast|mur}} * {{Z|tay}}: {{W-/ceribandin|tay|olit}} * {{Z|av}}: {{W-/ceribandin|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+/ceribandin|ay|achaku}} * {{Z|ain}}: {{W-/ceribandin|ain|エㇾㇺ|tr=erm}}, {{W-/ceribandin|ain|エㇾム|tr=ermu}}, {{W-/ceribandin|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+/ceribandin|az|siçan}} * {{Z|ban}}: {{W-/ceribandin|ban|bikul}}, {{W-/ceribandin|ban|jero ketut}} * {{Z|eu}}: {{W+/ceribandin|eu|sagu}} * {{Z|ba}}: {{W-/ceribandin|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-/ceribandin|be|мыш|m}}, {{W-/ceribandin|be|мы́шка|m}} * {{Z|bal}}: {{W-/ceribandin|bal|مشک|tr=mušk}}, {{W-/ceribandin|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+/ceribandin|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+/ceribandin|br|logodenn|m}} * {{Z|bg}}: {{W+/ceribandin|bg|ми́шка|m}}, {{W-/ceribandin|bg|мишо́к|n}} * {{Z|my}}: {{W+/ceribandin|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-/ceribandin|pcc|ranz}}, {{W-/ceribandin|pcc|waauc}} * {{Z|ccc}}: {{W-/ceribandin|ccc|kijsili}} * {{Z|ce}}: {{W-/ceribandin|ce|дахка}} * {{Z|cs}}: {{W+/ceribandin|cs|myš|m}}, {{W+/ceribandin|cs|myšák|n}} * {{Z|chr}}: {{W-/ceribandin|chr|ᏥᏍᏕᏥ}} * {{Z|ny}}: {{W-/ceribandin|ny|mbewa}} * {{Z|cic}}: {{W-/ceribandin|cic|pinti'}} * {{Z|zh}}: *: {{Z|yue}}: {{W-/ceribandin|yue|老鼠|tr=lou5 syu2}} *: {{Z|dng}}: {{W-/ceribandin|dng|лочў}} *: {{Z|hak}}: {{W-/ceribandin|hak|老鼠|tr=lo-chhú, ló-chhú, no-chhú}} *: {{Z|lzh}}: {{W-/ceribandin|lzh|鼠|tr=shǔ}}, {{W-/ceribandin|lzh|鼷|tr=xī}} *: {{Z|cmn}}: {{W+/ceribandin|cmn|老鼠|tr=lǎoshǔ}}, {{W+/ceribandin|cmn|鼠|tr=shǔ}}, {{W+/ceribandin|cmn|耗子|tr=hàozi}}, {{W+/ceribandin|cmn|小鼠|tr=xiǎoshǔ}}, {{W+/ceribandin|cmn|鼷|tr=xī}} *: {{Z|cdo}}: {{W-/ceribandin|cdo|老鼠|tr=lō̤-chṳ̄}} *: {{Z|nan}}: {{W+/ceribandin|nan|鳥鼠|tr=niáu-chhí, niáu-chhú}}, {{W+/ceribandin|nan|鼠|tr=chhí, chhú}}, {{W+/ceribandin|nan|錢鼠|tr=chîⁿ-chhí, chîⁿ-chhú}}, {{W-/ceribandin|nan|老鼠|tr=ló͘-chhír, ló͘-chhí, niáu-chhú, niáu-chhí, niáu-chhír}} *: {{Z|wuu}}: {{W-/ceribandin|wuu|老鼠}} * {{Z|ckt}}: {{W-/ceribandin|ckt|пипиӄыԓгын|tr=pipiqyḷgyn}} * {{Z|cv}}: {{W-/ceribandin|cv|шӑши|sc=Cyrl}} * {{Z|dlm}}: {{W-/ceribandin|dlm|surco|n}} * {{Z|da}}: {{W+/ceribandin|da|mus|g}} * {{Z|dv}}: {{W-/ceribandin|dv|މީދާ|sc=Thaa}} * {{Z|id}}: {{W+/ceribandin|id|tikus}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin|amûra [[komputer]]ê|id=amûr}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} * {{Z|tr}}: {{W+/ceribandin|tr|fare}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin}} {{werger-bin/ceribandin}} 5eohvbqbd3ntqxdioyaoxnnigr1s5a5 Gotûbêj:kîmyaçare 1 1359442 6793980 2026-10-14T18:23:06Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina nîqaşê ji rûpela [[Wîkîferheng:Daxwazên peyitandinê]] 6793980 wikitext text/x-wiki == Nîqaşa [[Wîkîferheng:Daxwazên peyitandinê|peyitandinê]] == {{arşîv-ser|dp|failed|link=Taybet:PermanentLink/6724619#kîmyaçare}} {{fake==|1= <s>[[kîmyaçare]]</s> }} Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:50, 23 îlon 2026 (UTC) :{{nepeyitandî}}--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:39, 29 îlon 2026 (UTC) {{arşîv-bin}} p3bi91ne7hrdvw8rtsjdi9555akldsa Gotûbêj:pofêldang 1 1359443 6793981 2026-10-14T18:23:06Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina nîqaşê ji rûpela [[Wîkîferheng:Daxwazên peyitandinê]] 6793981 wikitext text/x-wiki == Nîqaşa [[Wîkîferheng:Daxwazên peyitandinê|peyitandinê]] == {{arşîv-ser|dp|failed|link=Taybet:PermanentLink/6724619#pofêldang}} {{fake==|1= <s>[[pofêldang]]</s> }} Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 07:09, 10 îlon 2026 (UTC) :{{nepeyitandî}}--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:47, 4 çiriya pêşîn 2026 (UTC) {{arşîv-bin}} hbf3rqelkkgescusyizjllbqvkdkgp6 Gotûbêj:katdirêj 1 1359444 6793982 2026-10-14T18:23:06Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina nîqaşê ji rûpela [[Wîkîferheng:Daxwazên peyitandinê]] 6793982 wikitext text/x-wiki == Nîqaşa [[Wîkîferheng:Daxwazên peyitandinê|peyitandinê]] == {{arşîv-ser|dp|failed|link=Taybet:PermanentLink/6724619#katdirêj}} {{fake==|1= <s>[[katdirêj]]</s> }} Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 4 çiriya pêşîn 2026 (UTC) :{{nepeyitandî}}--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 4 çiriya pêşîn 2026 (UTC) {{arşîv-bin}} myhla05b80kdt0ywsqznkcuytc8zok9 Gotûbêj:inyennegirî 1 1359445 6793983 2026-10-14T18:23:06Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina nîqaşê ji rûpela [[Wîkîferheng:Daxwazên peyitandinê]] 6793983 wikitext text/x-wiki == Nîqaşa [[Wîkîferheng:Daxwazên peyitandinê|peyitandinê]] == {{arşîv-ser|dp|failed|link=Taybet:PermanentLink/6724619#inyennegirî}} {{fake==|1= <s>[[inyennegirî]]</s> }} Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 27 îlon 2026 (UTC) :{{nepeyitandî}}--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:38, 29 îlon 2026 (UTC) {{arşîv-bin}} 8zewlggeagcutwmn1fbofb7wxsyzj5x Gotûbêj:hinavbînî 1 1359446 6793984 2026-10-14T18:23:06Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina nîqaşê ji rûpela [[Wîkîferheng:Daxwazên peyitandinê]] 6793984 wikitext text/x-wiki == Nîqaşa [[Wîkîferheng:Daxwazên peyitandinê|peyitandinê]] == {{arşîv-ser|dp|failed|link=Taybet:PermanentLink/6724619#hinavbînî}} {{fake==|1= <s>[[hinavbînî]]</s> }} Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:23, 23 îlon 2026 (UTC) :{{nepeyitandî}}--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:39, 29 îlon 2026 (UTC) {{arşîv-bin}} ibsbvu0swualh57y0hopr6yd24d4622 Gotûbêj:kemoapoîzm 1 1359447 6793985 2026-10-14T18:23:06Z Wikihez 7344 [[MediaWiki:Gadget-aWa.js/belgekirin|aWa]]: Arşîvkirina nîqaşê ji rûpela [[Wîkîferheng:Daxwazên peyitandinê]] 6793985 wikitext text/x-wiki == Nîqaşa [[Wîkîferheng:Daxwazên peyitandinê|peyitandinê]] == {{arşîv-ser|dp|failed|link=Taybet:PermanentLink/6724619#kemoapoîzm}} {{fake==|1= <s>[[kemoapoîzm]]</s> }} Bnr. [[Wîkîferheng:Pîvekên tevlîkirinê|Krîterên tevlîkirinê]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:37, 29 îlon 2026 (UTC) :{{nepeyitandî}}--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:38, 29 îlon 2026 (UTC) {{arşîv-bin}} 78zptutxm3md14tzfqr3nr1wkrrxbyi Bikarhêner:Wikihez/Gadget-test/translation-adder.css 2 1359448 6794002 2026-10-14T18:30:43Z Wikihez 7344 Spacing for the translation adder form 6794002 css text/css /* Page: [[MediaWiki:Gadget-wikt.translation-adder.css]] */ .translation-adder-row label { margin-right: 0.8em; white-space: nowrap; } .translation-adder-row input[type="checkbox"] { margin-right: 0.2em; } .translation-adder-langname { color: var(--color-subtle, #54595d); } p13d1blq3foggie3y7lczi2eyz5inua 6794032 6794002 2026-10-14T19:20:25Z Wikihez 7344 Translation adder: language dropdown, code shown as its name, editor passed in by DefSideBoxes 6794032 css text/css /* Page: [[MediaWiki:Gadget-wikt.translation-adder.css]] */ .translation-adder-row label { margin-right: 0.8em; white-space: nowrap; } .translation-adder-row input[type="checkbox"] { margin-right: 0.2em; } .translation-adder-langname { color: var(--color-subtle, #54595d); } .translation-adder-langbox { position: relative; display: inline-block; } .translation-adder-menu { position: absolute; top: 100%; left: 0; z-index: 10; min-width: 14em; max-height: 20em; overflow-y: auto; margin: 0; padding: 2px 0; list-style: none; background: var(--background-color-base, #fff); color: var(--color-base, #202122); border: 1px solid var(--border-color-base, #a2a9b1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .translation-adder-menu li { margin: 0; padding: 2px 8px; cursor: pointer; white-space: nowrap; } .translation-adder-menu li:hover, .translation-adder-menu .translation-adder-menu-active { background: var(--background-color-progressive-subtle, #eaf3ff); } .translation-adder-menu-code { color: var(--color-subtle, #54595d); font-size: 85%; } 9qzgw0gi00ucjrz55sj0pfhegtszro5 6794043 6794032 2026-10-14T19:33:37Z Wikihez 7344 Translation adder: automatic nesting by code, line editing (✎ ×), highlighted pending lines 6794043 css text/css /* Page: [[MediaWiki:Gadget-wikt.translation-adder.css]] */ .translation-adder-row label { margin-right: 0.8em; white-space: nowrap; } .translation-adder-row input[type="checkbox"] { margin-right: 0.2em; } .translation-adder-langname { color: var(--color-subtle, #54595d); } .translation-adder-langbox { position: relative; display: inline-block; } .translation-adder-menu { position: absolute; top: 100%; left: 0; z-index: 10; min-width: 14em; max-height: 20em; overflow-y: auto; margin: 0; padding: 2px 0; list-style: none; background: var(--background-color-base, #fff); color: var(--color-base, #202122); border: 1px solid var(--border-color-base, #a2a9b1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .translation-adder-menu li { margin: 0; padding: 2px 8px; cursor: pointer; white-space: nowrap; } .translation-adder-menu li:hover, .translation-adder-menu .translation-adder-menu-active { background: var(--background-color-progressive-subtle, #eaf3ff); } .translation-adder-menu-code { color: var(--color-subtle, #54595d); font-size: 85%; } /* Lines added or changed since the last save. */ .translation-adder-new { background-color: var(--background-color-success-subtle, #dff2eb); outline: 2px dashed var(--border-color-success, #14866d); outline-offset: 1px; } /* Lines that the next save removes. */ .translation-adder-removed { text-decoration: line-through; background-color: var(--background-color-error-subtle, #ffe9e5); } .translation-adder-line-tools a { padding: 0 2px; font-size: 90%; } .translation-adder-line-form { margin: 2px 0 4px; } mnju73duartu49a8w0wt4s8mf9sxv1y 6794048 6794043 2026-10-14T19:36:38Z Wikihez 7344 Line preview without its paragraph; removed lines outlined red 6794048 css text/css /* Page: [[MediaWiki:Gadget-wikt.translation-adder.css]] */ .translation-adder-row label { margin-right: 0.8em; white-space: nowrap; } .translation-adder-row input[type="checkbox"] { margin-right: 0.2em; } .translation-adder-langname { color: var(--color-subtle, #54595d); } .translation-adder-langbox { position: relative; display: inline-block; } .translation-adder-menu { position: absolute; top: 100%; left: 0; z-index: 10; min-width: 14em; max-height: 20em; overflow-y: auto; margin: 0; padding: 2px 0; list-style: none; background: var(--background-color-base, #fff); color: var(--color-base, #202122); border: 1px solid var(--border-color-base, #a2a9b1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .translation-adder-menu li { margin: 0; padding: 2px 8px; cursor: pointer; white-space: nowrap; } .translation-adder-menu li:hover, .translation-adder-menu .translation-adder-menu-active { background: var(--background-color-progressive-subtle, #eaf3ff); } .translation-adder-menu-code { color: var(--color-subtle, #54595d); font-size: 85%; } /* Lines added or changed since the last save. */ .translation-adder-new { background-color: var(--background-color-success-subtle, #dff2eb); outline: 2px dashed var(--border-color-success, #14866d); outline-offset: 1px; } /* Lines that the next save removes. */ .translation-adder-removed { text-decoration: line-through; background-color: var(--background-color-error-subtle, #ffe9e5); outline-color: var(--border-color-error, #d73333); } .translation-adder-line-tools a { padding: 0 2px; font-size: 90%; } .translation-adder-line-form { margin: 2px 0 4px; } oye98pudal5g36wl04b2i88qg1jcd5u 6794053 6794048 2026-10-14T19:43:41Z Wikihez 7344 Each translation opens its template in the TemplateData dialog; + adds one in the same language; language menu on the page body 6794053 css text/css /* Page: [[MediaWiki:Gadget-wikt.translation-adder.css]] */ .translation-adder-row label { margin-right: 0.8em; white-space: nowrap; } .translation-adder-row input[type="checkbox"] { margin-right: 0.2em; } .translation-adder-langname { color: var(--color-subtle, #54595d); } .translation-adder-langbox { display: inline-block; } .translation-adder-menu { position: absolute; z-index: 1000; min-width: 14em; max-height: 20em; overflow-y: auto; margin: 0; padding: 2px 0; list-style: none; background: var(--background-color-base, #fff); color: var(--color-base, #202122); border: 1px solid var(--border-color-base, #a2a9b1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .translation-adder-menu li { margin: 0; padding: 2px 8px; cursor: pointer; white-space: nowrap; } .translation-adder-menu li:hover, .translation-adder-menu .translation-adder-menu-active { background: var(--background-color-progressive-subtle, #eaf3ff); } .translation-adder-menu-code { color: var(--color-subtle, #54595d); font-size: 85%; } /* Lines added or changed since the last save. */ .translation-adder-new { background-color: var(--background-color-success-subtle, #dff2eb); outline: 2px dashed var(--border-color-success, #14866d); outline-offset: 1px; } /* Lines that the next save removes. */ .translation-adder-removed { text-decoration: line-through; background-color: var(--background-color-error-subtle, #ffe9e5); outline-color: var(--border-color-error, #d73333); } .translation-adder-line-tools a { padding: 0 2px; font-size: 90%; } .translation-adder-term-edit { padding: 0 2px; font-size: 90%; } 9agk24n4k6x1tfjmy8p2gwppjalui55 6794059 6794053 2026-10-14T20:07:11Z Wikihez 7344 Draw a nested line as a dd, as the saved page has it 6794059 css text/css /* Page: [[MediaWiki:Gadget-wikt.translation-adder.css]] */ .translation-adder-row label { margin-right: 0.8em; white-space: nowrap; } .translation-adder-row input[type="checkbox"] { margin-right: 0.2em; } .translation-adder-langname { color: var(--color-subtle, #54595d); } .translation-adder-langbox { display: inline-block; } .translation-adder-menu { position: absolute; z-index: 1000; min-width: 14em; max-height: 20em; overflow-y: auto; margin: 0; padding: 2px 0; list-style: none; background: var(--background-color-base, #fff); color: var(--color-base, #202122); border: 1px solid var(--border-color-base, #a2a9b1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .translation-adder-menu li { margin: 0; padding: 2px 8px; cursor: pointer; white-space: nowrap; } .translation-adder-menu li:hover, .translation-adder-menu .translation-adder-menu-active { background: var(--background-color-progressive-subtle, #eaf3ff); } .translation-adder-menu-code { color: var(--color-subtle, #54595d); font-size: 85%; } /* Lines added or changed since the last save. */ .translation-adder-new { background-color: var(--background-color-success-subtle, #dff2eb); outline: 2px dashed var(--border-color-success, #14866d); outline-offset: 1px; } /* Lines that the next save removes. */ .translation-adder-removed { text-decoration: line-through; background-color: var(--background-color-error-subtle, #ffe9e5); outline-color: var(--border-color-error, #d73333); } .translation-adder-line-tools a { padding: 0 2px; font-size: 90%; } .translation-adder-term-edit { padding: 0 2px; font-size: 90%; } .translation-adder-wrap > li { list-style: none; } g6k1nivx4mf7yt0vzxzyvg1xrf8z6eo Modul:translations/ceribandin 828 1359449 6794020 2026-10-14T19:02:10Z Wikihez 7344 Test copy of en.wiktionary's Module:translations, with Werger- anchors and Kurmancî text and categories 6794020 Scribunto text/plain -- Forked from en.wiktionary's [[en:Module:translations]]: anchors are Werger-…, text and categories are Kurmancî. local export = {} local anchors_module = "Module:anchors" local debug_track_module = "Module:debug/track" local languages_module = "Module:languages" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local templatestyles_module = "Module:TemplateStyles" local utilities_module = "Module:utilities" local wikimedia_languages_module = "Module:wikimedia languages" local concat = table.concat local html_create = mw.html.create local insert = table.insert local load_data = mw.loadData local new_title = mw.title.new local require = require --[==[ 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_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function full_link(...) full_link = require(links_module).full_link return full_link(...) end local function get_link_page(...) get_link_page = require(links_module).get_link_page return get_link_page(...) end local function get_wikimedia_lang(...) get_wikimedia_lang = require(wikimedia_languages_module).getByCode return get_wikimedia_lang(...) end local function language_link(...) language_link = require(links_module).language_link return language_link(...) end local function normalize_anchor(...) normalize_anchor = require(anchors_module).normalize_anchor return normalize_anchor(...) end local function plain_link(...) plain_link = require(links_module).plain_link return plain_link(...) 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 split_on_slashes(...) split_on_slashes = require(links_module).split_on_slashes return split_on_slashes(...) end local function templatestyles(...) templatestyles = require(templatestyles_module) return templatestyles(...) end local function track(...) track = require(debug_track_module) return track(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local ku local function get_ku() ku, get_ku = require(languages_module).getByCode("ku"), nil return ku end local headword_data local function get_headword_data() headword_data, get_headword_data = load_data("Module:headword/data"), nil return headword_data end local parameters_data local function get_parameters_data() parameters_data, get_parameters_data = load_data("Module:parameters/data"), nil return parameters_data end local translations_data local function get_translations_data() translations_data, get_translations_data = load_data("Module:translations/data"), nil return translations_data end local function is_translation_subpage(pagename) if (headword_data or get_headword_data()).page.namespace ~= "" then return false elseif not pagename then pagename = (headword_data or get_headword_data()).encoded_pagename end return pagename:match("./Werger$") and true or false end local function canonical_pagename() local pagename = (headword_data or get_headword_data()).encoded_pagename return is_translation_subpage(pagename) and pagename:sub(1, -8) or pagename end local function interwiki(terminfo, term, lang, langcode) -- No interwiki link if term is empty/missing if not term or #term < 1 then terminfo.interwiki = false return end -- Percent-decode the term. term = decode_uri(terminfo.term, "PATH") -- Don't show an interwiki link if it's an invalid title. if not new_title(term) then terminfo.interwiki = false return end local interwiki_langcode = (translations_data or get_translations_data()).interwiki_langs[langcode] local wmlangs = interwiki_langcode and {get_wikimedia_lang(interwiki_langcode)} or lang:getWikimediaLanguages() -- Don't show the interwiki link if the language is not recognised by Wikimedia. if #wmlangs == 0 then terminfo.interwiki = false return end local sc = terminfo.sc local target_page = get_link_page(term, lang, sc) local split = split_on_slashes(target_page) if not split[1] then terminfo.interwiki = false return end target_page = split[1] local wmlangcode = wmlangs[1]:getCode() local interwiki_link = language_link{ lang = lang, sc = sc, term = wmlangcode .. ":" .. target_page, alt = "(" .. wmlangcode .. ")", tr = "-" } terminfo.interwiki = tostring(html_create("span") :addClass("tpos") :wikitext("&nbsp;" .. interwiki_link) ) end function export.show_terminfo(terminfo, check) local lang = terminfo.lang local langcode, langname = lang:getCode(), lang:getCanonicalName() -- Translations must be for mainspace languages. if not lang:hasType("regular") then error("Translations must be for attested and approved main-namespace languages.") else local disallowed = (translations_data or get_translations_data()).disallowed local err_msg = disallowed[langcode] if err_msg then error("Translations not allowed in " .. langname .. " (" .. langcode .. "). " .. langname .. " translations should " .. err_msg) end local fullcode = lang:getFullCode() if fullcode ~= langcode then err_msg = disallowed[fullcode] if err_msg then langname = lang:getFullName() error("Translations not allowed in " .. langname .. " (" .. fullcode .. "). " .. langname .. " translations should " .. err_msg) end end end local term = terminfo.term -- Check if there is a term. Don't show the interwiki link if there is nothing to link to. if not term then -- Track entries that don't provide a term. -- FIXME: This should be a category. track("translations/no term") track("translations/no term/" .. langcode) end if terminfo.interwiki then interwiki(terminfo, term, lang, langcode) end langcode = lang:getFullCode() if (translations_data or get_translations_data()).need_super[langcode] then local tr = terminfo.tr if tr ~= nil then terminfo.tr = tr:gsub("%d[%d%*%-]*%f[^%d%*]", "<sup>%0</sup>") end end terminfo.show_decorations = true local link = full_link(terminfo, "translation") local canonical_name = lang:getCanonicalName() local full_name = lang:getFullName() local categories = {"Werger bi " .. canonical_name} if canonical_name ~= full_name then insert(categories, "Werger bi " .. full_name) end if check then link = tostring(html_create("span") :addClass("ttbc") :tag("sup") :addClass("ttbc") :wikitext("(ji kerema xwe kontrol bike)") :done() :wikitext(" " .. link) ) insert(categories, "Kontrola wergerê") end return link .. format_categories(categories, ku or get_ku(), nil, canonical_pagename()) end -- Implements {{t}}, {{t+}}, {{t-check}} and {{t+check}}. function export.show(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["translation"]) local check = frame.args.check return export.show_terminfo({ lang = args[1], sc = args.sc, track_sc = true, term = args[2], alt = args.alt, id = args.id, genders = args[3], tr = args.tr, ts = args.ts, lit = args.lit, q = args.q, qq = args.qq, l = args.l, ll = args.ll, refs = args.ref, interwiki = frame.args.interwiki, }, check and check ~= "") end local function add_id(div, id) return id and div:attr("id", normalize_anchor("Werger-" .. id)) or div end -- Implements {{trans-top}} and part of {{trans-top-also}}. local function top(args, title, id, navhead) local column_width = (args["column-width"] == "wide" or args["column-width"] == "narrow") and "-" .. args["column-width"] or "" local column_count = args["stûn"] and "column-count:" .. args["stûn"] or nil local div = html_create("div") :addClass("NavFrame") :node(navhead) :tag("div") :addClass("NavContent") :tag("table") :addClass("translations") :attr("role", "presentation") :attr("data-gloss", title or "") :tag("tr") :tag("td") :addClass("translations-cell") :addClass("multicolumn-list" .. column_width) :attr("colspan", "3") :cssText(column_count) :allDone() div = add_id(div, id) return (tostring(div):gsub("</td></tr></table></div></div>$", "")) .. templatestyles("Module:translations/styles.css") end -- Entry point for {{trans-top}}. function export.top(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-top"]) local title = args[1] local id = args.id or title title = title and remove_links(title) return top(args, title, id, html_create("div") :addClass("NavHead") :css("text-align", "left") :wikitext((title or "Werger") .. (args[2] and "&nbsp;(" .. args[2] .. ")" or "")) ) end -- Entry point for {{checktrans-top}}. function export.check_top(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["checktrans-top"]) local text = "\n:''Divê tercimeyên li jêr bên kontrolkirin û li qutiyên munasib ên li jor bên bicihkirin.''\n" local header = html_create("div") :addClass("checktrans") :wikitext(text) local subtitle = args[1] local title = "Tercimeyên ku divê bên kontrolkirin" if subtitle then title = title .. "&zwnj;: \"" .. subtitle .. "\"" end -- No ID, since these should always accompany proper translation tables, and can't be trusted anyway (i.e. there's no use-case for links). return tostring(header) .. "\n" .. top(args, title, nil, html_create("div") :addClass("NavHead") :css("text-align", "left") :wikitext(title) ) end -- Implements {{trans-bottom}}. function export.bottom(frame) -- Check nothing is being passed as a parameter. process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-bottom"]) return "</table></div></div>" end -- Implements {{trans-see}} and part of {{trans-top-also}}. local function see(args, see_text) local navhead = html_create("div") :addClass("NavHead") :css("text-align", "left") :wikitext(args[1] .. " ") :tag("span") :css("font-weight", "normal") :wikitext("— ") :tag("i") :wikitext(see_text) :allDone() local terms, id = args[2], args.id if #terms == 0 then terms[1] = args[1] end for i = 1, #terms do local term_id = id[i] or id.default local data = { term = terms[i], id = term_id and "Werger-" .. term_id or "Werger", } terms[i] = plain_link(data) end return navhead:wikitext(concat(terms, ",&lrm; ")) end -- Entry point for {{trans-see}}. function export.see(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-see"]) local div = html_create("div") :addClass("pseudo") :addClass("NavFrame") :node(see(args, "binêre ")) return tostring(add_id(div, args.id.default or args[1])) end -- Entry point for {{trans-top-also}}. function export.top_also(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-top-also"]) local navhead = see(args, "herwiha binêre ") local title = args[1] local id = args.id.default or title title = remove_links(title) return top(args, title, id, navhead) end -- Implements {{translation subpage}}. function export.subpage(frame) process_params(frame:getParent().args, (parameters_data or get_parameters_data())["translation subpage"]) if not is_translation_subpage() then error("This template should only be used on translation subpages, which have titles that end with '/Werger'.") end -- "Translation subpages" category is handled by {{trans-top}}. return ("''Ev rûpel tercimeyên ''%s'' dihewîne. Ji bo agahiyên din li gotara serekî binêre.''"):format(full_link{ lang = ku or get_ku(), term = canonical_pagename(), }) end -- Implements {{t-needed}}. function export.needed(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["t-needed"]) local lang, category = args[1], "" local span = html_create("span") :addClass("trreq") :attr("data-lang", lang:getCode()) :tag("i") :wikitext("ji kerema xwe vê tercimeyê lê zêde bike") :done() if not args.nocat then local type, sort = args[2], args.sort if type == "quote" then category = "Daxwazên tercimeya jêgirtinên " .. lang:getCanonicalName() elseif type == "usex" then category = "Daxwazên tercimeya nimûneyên " .. lang:getCanonicalName() else category = "Daxwazên tercimeyê bo " .. lang:getCanonicalName() lang = ku or get_ku() end category = format_categories(category, lang, sort, not sort and canonical_pagename() or nil) end return tostring(span) .. category end -- Implements {{no equivalent translation}}. function export.no_equivalent(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["no equivalent translation"]) local text = "di " .. args[1]:getCanonicalName() .. " de kelîmeyeke beramber tune" if not args.noend then text = text .. ", lê binêre" end return tostring(html_create("i"):wikitext(text)) end -- Implements {{no attested translation}}. function export.no_attested(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["no attested translation"]) local langname = args[1]:getCanonicalName() local text = "di " .. langname .. " de kelîmeyeke peyitandî tune" local category = "" if not args.noend then text = text .. ", lê binêre" local sort = args.sort category = format_categories("Wergerên nepeyitandî bi " .. langname, ku or get_ku(), sort, not sort and canonical_pagename() or nil) end return tostring(html_create("i"):wikitext(text)) .. category end -- Implements {{not used}}. function export.not_used(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["not used"]) return tostring(html_create("i"):wikitext("di " .. args[1]:getCanonicalName() .. " de " .. (args[2] or "nayê bikaranîn"))) end return export 2u5h6a0uplshq5mroj1jzzj0jk9ri2i 6794080 6794020 2026-10-14T21:24:45Z Wikihez 7344 guman=erê asks for a check, as {{t-check}} does 6794080 Scribunto text/plain -- Forked from en.wiktionary's [[en:Module:translations]]: anchors are Werger-…, text and categories are Kurmancî. local export = {} local anchors_module = "Module:anchors" local debug_track_module = "Module:debug/track" local languages_module = "Module:languages" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local templatestyles_module = "Module:TemplateStyles" local utilities_module = "Module:utilities" local wikimedia_languages_module = "Module:wikimedia languages" local concat = table.concat local html_create = mw.html.create local insert = table.insert local load_data = mw.loadData local new_title = mw.title.new local require = require --[==[ 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_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function full_link(...) full_link = require(links_module).full_link return full_link(...) end local function get_link_page(...) get_link_page = require(links_module).get_link_page return get_link_page(...) end local function get_wikimedia_lang(...) get_wikimedia_lang = require(wikimedia_languages_module).getByCode return get_wikimedia_lang(...) end local function language_link(...) language_link = require(links_module).language_link return language_link(...) end local function normalize_anchor(...) normalize_anchor = require(anchors_module).normalize_anchor return normalize_anchor(...) end local function plain_link(...) plain_link = require(links_module).plain_link return plain_link(...) 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 split_on_slashes(...) split_on_slashes = require(links_module).split_on_slashes return split_on_slashes(...) end local function templatestyles(...) templatestyles = require(templatestyles_module) return templatestyles(...) end local function track(...) track = require(debug_track_module) return track(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local ku local function get_ku() ku, get_ku = require(languages_module).getByCode("ku"), nil return ku end local headword_data local function get_headword_data() headword_data, get_headword_data = load_data("Module:headword/data"), nil return headword_data end local parameters_data local function get_parameters_data() parameters_data, get_parameters_data = load_data("Module:parameters/data"), nil return parameters_data end local translations_data local function get_translations_data() translations_data, get_translations_data = load_data("Module:translations/data"), nil return translations_data end local function is_translation_subpage(pagename) if (headword_data or get_headword_data()).page.namespace ~= "" then return false elseif not pagename then pagename = (headword_data or get_headword_data()).encoded_pagename end return pagename:match("./Werger$") and true or false end local function canonical_pagename() local pagename = (headword_data or get_headword_data()).encoded_pagename return is_translation_subpage(pagename) and pagename:sub(1, -8) or pagename end local function interwiki(terminfo, term, lang, langcode) -- No interwiki link if term is empty/missing if not term or #term < 1 then terminfo.interwiki = false return end -- Percent-decode the term. term = decode_uri(terminfo.term, "PATH") -- Don't show an interwiki link if it's an invalid title. if not new_title(term) then terminfo.interwiki = false return end local interwiki_langcode = (translations_data or get_translations_data()).interwiki_langs[langcode] local wmlangs = interwiki_langcode and {get_wikimedia_lang(interwiki_langcode)} or lang:getWikimediaLanguages() -- Don't show the interwiki link if the language is not recognised by Wikimedia. if #wmlangs == 0 then terminfo.interwiki = false return end local sc = terminfo.sc local target_page = get_link_page(term, lang, sc) local split = split_on_slashes(target_page) if not split[1] then terminfo.interwiki = false return end target_page = split[1] local wmlangcode = wmlangs[1]:getCode() local interwiki_link = language_link{ lang = lang, sc = sc, term = wmlangcode .. ":" .. target_page, alt = "(" .. wmlangcode .. ")", tr = "-" } terminfo.interwiki = tostring(html_create("span") :addClass("tpos") :wikitext("&nbsp;" .. interwiki_link) ) end function export.show_terminfo(terminfo, check) local lang = terminfo.lang local langcode, langname = lang:getCode(), lang:getCanonicalName() -- Translations must be for mainspace languages. if not lang:hasType("regular") then error("Translations must be for attested and approved main-namespace languages.") else local disallowed = (translations_data or get_translations_data()).disallowed local err_msg = disallowed[langcode] if err_msg then error("Translations not allowed in " .. langname .. " (" .. langcode .. "). " .. langname .. " translations should " .. err_msg) end local fullcode = lang:getFullCode() if fullcode ~= langcode then err_msg = disallowed[fullcode] if err_msg then langname = lang:getFullName() error("Translations not allowed in " .. langname .. " (" .. fullcode .. "). " .. langname .. " translations should " .. err_msg) end end end local term = terminfo.term -- Check if there is a term. Don't show the interwiki link if there is nothing to link to. if not term then -- Track entries that don't provide a term. -- FIXME: This should be a category. track("translations/no term") track("translations/no term/" .. langcode) end if terminfo.interwiki then interwiki(terminfo, term, lang, langcode) end langcode = lang:getFullCode() if (translations_data or get_translations_data()).need_super[langcode] then local tr = terminfo.tr if tr ~= nil then terminfo.tr = tr:gsub("%d[%d%*%-]*%f[^%d%*]", "<sup>%0</sup>") end end terminfo.show_decorations = true local link = full_link(terminfo, "translation") local canonical_name = lang:getCanonicalName() local full_name = lang:getFullName() local categories = {"Werger bi " .. canonical_name} if canonical_name ~= full_name then insert(categories, "Werger bi " .. full_name) end if check then link = tostring(html_create("span") :addClass("ttbc") :tag("sup") :addClass("ttbc") :wikitext("(ji kerema xwe kontrol bike)") :done() :wikitext(" " .. link) ) insert(categories, "Kontrola wergerê") end return link .. format_categories(categories, ku or get_ku(), nil, canonical_pagename()) end -- Implements {{t}}, {{t+}}, {{t-check}} and {{t+check}}. function export.show(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["translation"]) -- guman=erê is {{W}}'s way of asking for a check. local check = frame.args.check or (args.guman == "erê" and "1" or nil) return export.show_terminfo({ lang = args[1], sc = args.sc, track_sc = true, term = args[2], alt = args.alt, id = args.id, genders = args[3], tr = args.tr, ts = args.ts, lit = args.lit, q = args.q, qq = args.qq, l = args.l, ll = args.ll, refs = args.ref, interwiki = frame.args.interwiki, }, check and check ~= "") end local function add_id(div, id) return id and div:attr("id", normalize_anchor("Werger-" .. id)) or div end -- Implements {{trans-top}} and part of {{trans-top-also}}. local function top(args, title, id, navhead) local column_width = (args["column-width"] == "wide" or args["column-width"] == "narrow") and "-" .. args["column-width"] or "" local column_count = args["stûn"] and "column-count:" .. args["stûn"] or nil local div = html_create("div") :addClass("NavFrame") :node(navhead) :tag("div") :addClass("NavContent") :tag("table") :addClass("translations") :attr("role", "presentation") :attr("data-gloss", title or "") :tag("tr") :tag("td") :addClass("translations-cell") :addClass("multicolumn-list" .. column_width) :attr("colspan", "3") :cssText(column_count) :allDone() div = add_id(div, id) return (tostring(div):gsub("</td></tr></table></div></div>$", "")) .. templatestyles("Module:translations/styles.css") end -- Entry point for {{trans-top}}. function export.top(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-top"]) local title = args[1] local id = args.id or title title = title and remove_links(title) return top(args, title, id, html_create("div") :addClass("NavHead") :css("text-align", "left") :wikitext((title or "Werger") .. (args[2] and "&nbsp;(" .. args[2] .. ")" or "")) ) end -- Entry point for {{checktrans-top}}. function export.check_top(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["checktrans-top"]) local text = "\n:''Divê tercimeyên li jêr bên kontrolkirin û li qutiyên munasib ên li jor bên bicihkirin.''\n" local header = html_create("div") :addClass("checktrans") :wikitext(text) local subtitle = args[1] local title = "Tercimeyên ku divê bên kontrolkirin" if subtitle then title = title .. "&zwnj;: \"" .. subtitle .. "\"" end -- No ID, since these should always accompany proper translation tables, and can't be trusted anyway (i.e. there's no use-case for links). return tostring(header) .. "\n" .. top(args, title, nil, html_create("div") :addClass("NavHead") :css("text-align", "left") :wikitext(title) ) end -- Implements {{trans-bottom}}. function export.bottom(frame) -- Check nothing is being passed as a parameter. process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-bottom"]) return "</table></div></div>" end -- Implements {{trans-see}} and part of {{trans-top-also}}. local function see(args, see_text) local navhead = html_create("div") :addClass("NavHead") :css("text-align", "left") :wikitext(args[1] .. " ") :tag("span") :css("font-weight", "normal") :wikitext("— ") :tag("i") :wikitext(see_text) :allDone() local terms, id = args[2], args.id if #terms == 0 then terms[1] = args[1] end for i = 1, #terms do local term_id = id[i] or id.default local data = { term = terms[i], id = term_id and "Werger-" .. term_id or "Werger", } terms[i] = plain_link(data) end return navhead:wikitext(concat(terms, ",&lrm; ")) end -- Entry point for {{trans-see}}. function export.see(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-see"]) local div = html_create("div") :addClass("pseudo") :addClass("NavFrame") :node(see(args, "binêre ")) return tostring(add_id(div, args.id.default or args[1])) end -- Entry point for {{trans-top-also}}. function export.top_also(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["trans-top-also"]) local navhead = see(args, "herwiha binêre ") local title = args[1] local id = args.id.default or title title = remove_links(title) return top(args, title, id, navhead) end -- Implements {{translation subpage}}. function export.subpage(frame) process_params(frame:getParent().args, (parameters_data or get_parameters_data())["translation subpage"]) if not is_translation_subpage() then error("This template should only be used on translation subpages, which have titles that end with '/Werger'.") end -- "Translation subpages" category is handled by {{trans-top}}. return ("''Ev rûpel tercimeyên ''%s'' dihewîne. Ji bo agahiyên din li gotara serekî binêre.''"):format(full_link{ lang = ku or get_ku(), term = canonical_pagename(), }) end -- Implements {{t-needed}}. function export.needed(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["t-needed"]) local lang, category = args[1], "" local span = html_create("span") :addClass("trreq") :attr("data-lang", lang:getCode()) :tag("i") :wikitext("ji kerema xwe vê tercimeyê lê zêde bike") :done() if not args.nocat then local type, sort = args[2], args.sort if type == "quote" then category = "Daxwazên tercimeya jêgirtinên " .. lang:getCanonicalName() elseif type == "usex" then category = "Daxwazên tercimeya nimûneyên " .. lang:getCanonicalName() else category = "Daxwazên tercimeyê bo " .. lang:getCanonicalName() lang = ku or get_ku() end category = format_categories(category, lang, sort, not sort and canonical_pagename() or nil) end return tostring(span) .. category end -- Implements {{no equivalent translation}}. function export.no_equivalent(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["no equivalent translation"]) local text = "di " .. args[1]:getCanonicalName() .. " de kelîmeyeke beramber tune" if not args.noend then text = text .. ", lê binêre" end return tostring(html_create("i"):wikitext(text)) end -- Implements {{no attested translation}}. function export.no_attested(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["no attested translation"]) local langname = args[1]:getCanonicalName() local text = "di " .. langname .. " de kelîmeyeke peyitandî tune" local category = "" if not args.noend then text = text .. ", lê binêre" local sort = args.sort category = format_categories("Wergerên nepeyitandî bi " .. langname, ku or get_ku(), sort, not sort and canonical_pagename() or nil) end return tostring(html_create("i"):wikitext(text)) .. category end -- Implements {{not used}}. function export.not_used(frame) local args = process_params(frame:getParent().args, (parameters_data or get_parameters_data())["not used"]) return tostring(html_create("i"):wikitext("di " .. args[1]:getCanonicalName() .. " de " .. (args[2] or "nayê bikaranîn"))) end return export mp7tp288mnaa2rmghc4azgwhhdrs5my Şablon:werger-bnr/ceribandin 10 1359450 6794023 2026-10-14T19:02:26Z Wikihez 7344 Test copy using Modul:translations/ceribandin 6794023 wikitext text/x-wiki {{#invoke:translations/ceribandin|see}}<noinclude> Test copy of {{ş|werger-bnr}} (en.wiktionary's trans-see). </noinclude> 1u5aj0dpjvidrhvp92jgnzuvn6057q8 Şablon:W/ceribandin 10 1359451 6794024 2026-10-14T19:02:31Z Wikihez 7344 Test copy using Modul:translations/ceribandin 6794024 wikitext text/x-wiki {{#invoke:translations/ceribandin|show}}<noinclude> Test copy of {{ş|W}} (en.wiktionary's t). </noinclude> equ80lo6ojy2q458c96qtd1zfbknm3j 6794049 6794024 2026-10-14T19:41:28Z Wikihez 7344 TemplateData for the translation template, read by the template dialog 6794049 wikitext text/x-wiki {{#invoke:translations/ceribandin|show}}<noinclude> Test copy of {{ş|W}} (en.wiktionary's t). <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string" }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string" }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id" ] } </templatedata> </noinclude> 8r9d2xumoo9poix2a96t0qjpu22aor6 6794081 6794049 2026-10-14T21:24:51Z Wikihez 7344 TemplateData: aliases cuda, dif, R and the guman parameter 6794081 wikitext text/x-wiki {{#invoke:translations/ceribandin|show}}<noinclude> Test copy of {{ş|W}} (en.wiktionary's t). <templatedata> { "description": "Tercimeyeke kelîmeyê bi zimanekî din, di qutiya wergerê de piştî {{Z}}: * {{Z|de}}: {{W+|de|Wasser|n}}.", "format": "inline", "params": { "1": { "label": "Koda zimanî", "description": "Koda zimanê tercimeyê, wek de an fr.", "type": "string", "required": true, "example": "de" }, "2": { "label": "Tercime", "description": "Kelîme bi wî zimanî; rûpela wê ya li Wîkîferhengê.", "type": "string", "required": true, "suggested": true, "example": "Wasser" }, "3": { "label": "Cins an hejmar", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string", "suggested": true, "example": "n" }, "4": { "label": "Cins an hejmara duyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "5": { "label": "Cins an hejmara sêyem", "description": "Kod ji [[Modul:gender and number/data]]: n (nêr), m (mê), nt (nêtar), g (giştî), y (yekjimar), p (pirjimar), d (dual), impf, pf; bi hev re wek n-p.", "type": "string" }, "alt": { "label": "Forma ku tê nîşandan", "description": "Heke kelîme bi şikleke din ji navê rûpelê bê nîşandan, wek bi nîşanên dengdêran.", "type": "string", "aliases": [ "cuda", "dif" ] }, "tr": { "label": "Transliterasyon", "description": "Kelîme bi herfên latînî, ji bo zimanên bi alfabeyeke din.", "type": "string", "aliases": [ "R" ] }, "ts": { "label": "Transkripsiyon", "description": "Xwendina kelîmeyê, heke ji transliterasyonê cuda be.", "type": "string" }, "lit": { "label": "Maneya herfî", "description": "Maneya kelîme bi kelîme ya tercimeyê.", "type": "string" }, "sc": { "label": "Koda alfabeyê", "description": "Heke otomatîk nehatibe tespîtkirin, wek Cyrl an Arab.", "type": "string" }, "id": { "label": "Id ya maneyê", "description": "Id ya maneyê li ser rûpela tercimeyê ({{maneid}}), da ku lînk biçe wê maneyê.", "type": "string" }, "guman": { "label": "Guman", "description": "erê: tercime hê ne kontrolkirî ye; li ber wê \"ji kerema xwe kontrol bike\" tê nivîsîn û rûpel dikeve Kategorî:Kontrola wergerê.", "type": "string", "suggestedvalues": [ "erê" ] } }, "paramOrder": [ "1", "2", "3", "4", "5", "alt", "tr", "ts", "lit", "sc", "id", "guman" ] } </templatedata> </noinclude> hzn9zob0jpqf0xol8ywdle53zoyz093 Bikarhêner:Wikihez/Gadget-test/targeted-translations.js 2 1359452 6794033 2026-10-14T19:20:31Z Wikihez 7344 Fork of en.wiktionary's TargetedTranslations: preferred languages shown on translation box headings 6794033 javascript text/javascript // <nowiki> /* * Page: [[MediaWiki:Gadget-wikt.targeted-translations.js]] * * Lets a reader mark preferred languages in a translation box; their translations are then shown on every * box's heading ("— Almanî: Maus; Rusî: мышь"), so they can be read without opening the box. Forked from * en.wiktionary's [[en:MediaWiki:Gadget-TargetedTranslations.js]]. Languages are kept by their code (from * {{Z}}), or by the name before the colon on a line without one, in this browser's localStorage. */ (function () { "use strict"; var STORAGE_KEY = "wikt-targeted-translations"; var LINES = "td.translations-cell li, td.translations-cell dd, div.translations li, div.translations dd"; var BOXES = "table.translations, div.translations"; var ICONS = { unselected: '<svg xmlns="http://www.w3.org/2000/svg" width="25" height="17" viewBox="0 0 20 20" fill="currentColor">' + '<path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z"/></svg>', selected: '<svg xmlns="http://www.w3.org/2000/svg" width="25" height="17" viewBox="0 0 20 20" fill="currentColor">' + '<path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z"/></svg>' }; var MSG = { select: "Zimanên bijarte hilbijêre", save: "Zimanên bijarte qeyd bike", clear: "Hemûyan rake" }; var preferred = new Set(mw.storage.getObject(STORAGE_KEY) || []); function store() { mw.storage.setObject(STORAGE_KEY, Array.from(preferred)); } // The line's own label: "Almanî" from "Almanî: Maus". function labelOf(line) { var text = line.textContent; var i = text.indexOf(":"); return i === -1 ? "" : text.slice(0, i).trim(); } // "de" from {{Z|de}}, else the label. function keyOf(line) { for (var node = line.firstElementChild; node; node = node.nextElementSibling) { var m = /(?:^|\s)trad-(\S+)/.exec(node.className); if (m) { return m[1]; } if (node.tagName !== "SPAN") { break; } } return labelOf(line); } // The HTML of a line after its "Language:" label, without the lines nested under it. function translationsOf(line) { var div = document.createElement("div"); div.innerHTML = line.innerHTML; $(div).children("ul, dl").remove(); $(div).find(".tt-icon").remove(); var walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT); var node; while ((node = walker.nextNode())) { var i = node.data.indexOf(":"); if (i !== -1) { var range = document.createRange(); range.setStart(div, 0); range.setEnd(node, i + 1); range.deleteContents(); break; } } return div.innerHTML.trim(); } // Calls back with each line that has translations and its key; a nested line's key is "zh>cmn". function eachLine(box, callback) { Array.prototype.forEach.call(box.querySelectorAll(LINES), function (line) { if ($(line).closest(".translation-adder-row").length || !labelOf(line)) { return; } var key = keyOf(line); var outer = line.parentElement && line.parentElement.parentElement; if (outer && outer.tagName === "LI" && box.contains(outer)) { key = keyOf(outer) + ">" + key; } callback(line, key); }); } function headOf(box) { return $(box).closest(".NavFrame").children(".NavHead")[0]; } // Shows the preferred languages' translations on each box's heading. function render(boxes) { boxes.forEach(function (box) { var head = headOf(box); if (!head) { return; } $(head).children(".targeted-translations").remove(); var parts = []; eachLine(box, function (line, key) { if (!preferred.has(key)) { return; } var html = translationsOf(line); if (!html) { return; } var label = labelOf(line); var outer = key.indexOf(">") !== -1 ? labelOf(line.parentElement.parentElement) : ""; parts.push($("<span>").text((outer ? outer + " (" + label + ")" : label) + ": ").append(html)); }); if (!parts.length) { return; } var $wrapper = $("<span class='targeted-translations'>").append(" — "); parts.forEach(function ($part, i) { $wrapper.append(i ? "; " : "", $part); }); $(head).append($wrapper); }); } function setIcon(icon, on) { icon.classList.toggle("tt-icon-selected", on); icon.innerHTML = on ? ICONS.selected : ICONS.unselected; } var allBoxes = []; function enterSelectMode(box, $select, $clear) { // One box at a time: another in select mode is saved first. $(".tt-button-select").not($select).filter(function () { return $(this).text() === MSG.save; }).trigger("click"); $select.text(MSG.save); $clear.parent().show(); eachLine(box, function (line, key) { var icon = document.createElement("span"); icon.className = "tt-icon"; icon.setAttribute("role", "button"); icon.title = key; setIcon(icon, preferred.has(key)); icon.addEventListener("click", function () { if (preferred.has(key)) { preferred.delete(key); } else { preferred.add(key); } setIcon(icon, preferred.has(key)); }); line.insertBefore(icon, line.firstChild); }); } function exitSelectMode(box, $select, $clear) { store(); $(box).find(".tt-icon").remove(); $select.text(MSG.select); $clear.parent().hide(); render(allBoxes); } function setUp(box) { if (box.dataset.targetedTranslations) { return; } box.dataset.targetedTranslations = "1"; allBoxes.push(box); var $select = $("<a role='button' class='tt-button-select'>").text(MSG.select); var $clear = $("<a role='button' class='tt-button-clear'>").text(MSG.clear); var $controls = $("<span class='targeted-translations-select'>").append( "[", $select, "]", $("<span>").hide().append(" [", $clear, "]")); if (box.tagName === "TABLE") { $("<tr>").append($("<td colspan='3' class='targeted-translations-select-cell'>").append($controls)) .prependTo($(box).children("tbody").first().length ? $(box).children("tbody").first() : $(box)); } else { $("<div class='targeted-translations-select-cell'>").append($controls).insertBefore(box); } $select.on("click", function () { if ($select.text() === MSG.select) { enterSelectMode(box, $select, $clear); } else { exitSelectMode(box, $select, $clear); } }); $clear.on("click", function () { preferred.clear(); $(box).find(".tt-icon").each(function () { setIcon(this, false); }); }); } mw.hook("wikipage.content").add(function ($content) { var boxes = $content.find(BOXES).get(); boxes.forEach(setUp); render(boxes); }); }()); // </nowiki> c9hiqwu97cnupniiuzh2dtwy5971o5f Bikarhêner:Wikihez/Gadget-test/targeted-translations.css 2 1359453 6794034 2026-10-14T19:20:36Z Wikihez 7344 Fork of en.wiktionary's TargetedTranslations: preferred languages shown on translation box headings 6794034 css text/css /* Page: [[MediaWiki:Gadget-wikt.targeted-translations.css]] */ .targeted-translations-select-cell { font-size: 85%; } .targeted-translations-select a { cursor: pointer; } .tt-icon { cursor: pointer; color: var(--color-progressive, #36c); } .tt-icon svg { vertical-align: text-top; } .targeted-translations { font-weight: normal; } 03flkwrpdxxtu0zs4ubdo1u06bfqqiw Bikarhêner:Wikihez/ceribandin/werger 2 1359454 6794038 2026-10-14T19:20:59Z Wikihez 7344 Test entry for the Werger pencil (copy of mişk with the test templates) 6794038 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} === Navdêr === [[Wêne:Micromys minutus (aka).jpg|thumb|Mişkek (1)]] [[Wêne:3-Tasten-Maus Microsoft.jpg|thumb|'''Mişkekî''' kompûterê (2)]] {{wp|Mişk (cudakirin)|Mişk}} {{navdêr|ku|z=n}} # {{f|ku|guhandar}} [[heywan|Heywanekî]] [[biçûk]] yê [[kirîner]] e û [[şibîn|dişibe]] [[cird]]an lê [[hûrik]]tir e. #: {{mk|ku|Kitik ne li mal e, navê '''mişkî''' Evdal e.}} # {{maneid|ku|informatîk}} {{f|ku|informatîk}} [[amûr|Amûrekî]] [[kompûter]]ê ye ku mirov pê [[nivîs]] û [[grafîk]]an [[livandin|dilivîne]]. ==== Tewandin ==== {{ku-tewîn-nav|nêr}} ==== Bi alfabeyên din ==== * {{ku-ar|مشک}} * {{ku-kr|мьшк}} ==== Formên alternatîv ==== * {{hw|ku|mihşk}} ==== Jê ==== {{kol3|ku | mişkane | mişke | mişko | mişkok }} ==== Etîmolojî ==== {{etîmon|ku|:drşnd|ine-pro:*múh₂s|dar=1}} Ji {{ji|ine-pro|ku|*múh₂s}}. Hevreha {{hv|ckb|مشک}}, [[kurdiya başûrî]] û lekî {{q|sdh|müş|sc=Latn}}, {{hv|bal|موشک‎|tr=muşk}} û {{g|bal|موشک‎|tr=mûşk}}, {{hv|pal||muşk|sc=Latn}}, {{hv|fa|موش|tr=mûş}}, {{hv|os|мыст|tr=mist}} û {{g|os|мистӕ|tr=mistæ}}, {{hv|ae|𐬨𐬏𐬱|tr=mūš}}, {{hv|sa|मूष्|tr=mū́ṣ}}, {{hv|hi|मूस|tr=mūs}}, {{hv|bg|мишка|tr=mişka}}, {{hv|ru|мыш|tr=miš}}, {{hv|cs|myš}}, {{hv|grc|μῦς|mûs}}, {{hv|la|mūs}}, {{hv|en|mouse}}, {{hv|de|Maus}}, danmarkî/norwecî/swêdî ''[[mus]]'', {{hv|hy|մուկ|tr=muk}}, albanî ''[[mi]]''... Herfa K li dawiya peyva kurmancî nîşana [[biçûk]]kirinê ([[dîmînutîv]]) Ye ji ber ku mişk [[heywan]]ekî [[hûrik]] e. Peyvên [[tatî]] {{g|fa|مورا}} (mûra/mora), talîşî {{g|fa|موره‎}} (more/morê), zazakî ''[[merre]]'', goranî/hewramî ''[[miłe]]'' û xotanî ''mûle-'' ji eynî rehî ne. Di wan de pêşî S/Ş bûye R û paşî di goranî û xotanî de R bûye L/Ł. Bi heman awayî ''[[mūs]]'' ya latînî di spanî de bûye ''[[mur]]'' û di portugalî de bûye ''[[mure]]''. Bi maneya "amûra kompûterê" {{wergirtin|en|ku|mouse|hûr=1}}. {{veşêre-ser|Çavkanî}} * {{Abayev|r= 142-143}} * {{Bailey|r= 337}} * {{Chyet|r= 393}} * {{Horn|r= 223}} * {{MacKenzie|r= 129}} * {{Lubotsky|r=752}} * {{Tsabolov|r=669-670}} * {{Etymonline}} {{veşêre-bin}} ==== Bide ber ==== {{kol3|ku | meşk | misk | hişk | pişk | rişk }} ==== Bi zaravayên din ==== * {{Z|ckb}}: {{W-/ceribandin|ckb|mişk|sc=Latn}} * {{Z|sdh}}: {{W-/ceribandin|sdh|müş|sc=Latn}} * {{Z|lki}}: {{W-/ceribandin|lki|müş|sc=Latn}} * {{Z|hac}}: {{W-/ceribandin|hac|miłe|sc=Latn}} * {{Z|zza}}: {{W+/ceribandin|zza|merre}} ==== Werger ==== {{werger-ser/ceribandin|heywanê biçûk ê ku [[penîr]] dixwe|1}} * {{Z|ab}}: {{W-/ceribandin|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+/ceribandin|af|muis}} * {{Z|sq}}: {{W+/ceribandin|sq|mi|n}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} *: {{Z|gsw}}: {{W-/ceribandin|gsw|Muus|m}} * {{Z|am}}: {{W-/ceribandin|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-/ceribandin|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-/ceribandin|an|zorz}} * {{Z|rup}}: {{W-/ceribandin|rup|shoaric}} * {{Z|aer}}: {{W-/ceribandin|aer|thakwere}} * {{Z|as}}: {{W-/ceribandin|as|নিগনি}} * {{Z|ast}}: {{W+/ceribandin|ast|ratu|n}}, {{W+/ceribandin|ast|ratón|n}}, {{W+/ceribandin|ast|mur}} * {{Z|tay}}: {{W-/ceribandin|tay|olit}} * {{Z|av}}: {{W-/ceribandin|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+/ceribandin|ay|achaku}} * {{Z|ain}}: {{W-/ceribandin|ain|エㇾㇺ|tr=erm}}, {{W-/ceribandin|ain|エㇾム|tr=ermu}}, {{W-/ceribandin|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+/ceribandin|az|siçan}} * {{Z|ban}}: {{W-/ceribandin|ban|bikul}}, {{W-/ceribandin|ban|jero ketut}} * {{Z|eu}}: {{W+/ceribandin|eu|sagu}} * {{Z|ba}}: {{W-/ceribandin|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-/ceribandin|be|мыш|m}}, {{W-/ceribandin|be|мы́шка|m}} * {{Z|bal}}: {{W-/ceribandin|bal|مشک|tr=mušk}}, {{W-/ceribandin|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+/ceribandin|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+/ceribandin|br|logodenn|m}} * {{Z|bg}}: {{W+/ceribandin|bg|ми́шка|m}}, {{W-/ceribandin|bg|мишо́к|n}} * {{Z|my}}: {{W+/ceribandin|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-/ceribandin|pcc|ranz}}, {{W-/ceribandin|pcc|waauc}} * {{Z|ccc}}: {{W-/ceribandin|ccc|kijsili}} * {{Z|ce}}: {{W-/ceribandin|ce|дахка}} * {{Z|cs}}: {{W+/ceribandin|cs|myš|m}}, {{W+/ceribandin|cs|myšák|n}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin|amûra [[komputer]]ê|2|id=amûr}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} {{werger-bin/ceribandin}} 7ks2ua1klixl8ee5nhmubmfcqhg04z2 6794041 6794038 2026-10-14T19:31:00Z Wikihez 7344 Kurmancî/Navdêr: +mandarînî: [[test]], beşa «Etîmolojî» hate rakirin (DefSideBoxes) 6794041 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} === Navdêr === [[Wêne:Micromys minutus (aka).jpg|thumb|Mişkek (1)]] [[Wêne:3-Tasten-Maus Microsoft.jpg|thumb|'''Mişkekî''' kompûterê (2)]] {{wp|Mişk (cudakirin)|Mişk}} {{navdêr|ku|z=n}} # {{f|ku|guhandar}} [[heywan|Heywanekî]] [[biçûk]] yê [[kirîner]] e û [[şibîn|dişibe]] [[cird]]an lê [[hûrik]]tir e. #: {{mk|ku|Kitik ne li mal e, navê '''mişkî''' Evdal e.}} # {{maneid|ku|informatîk}} {{f|ku|informatîk}} [[amûr|Amûrekî]] [[kompûter]]ê ye ku mirov pê [[nivîs]] û [[grafîk]]an [[livandin|dilivîne]]. ==== Tewandin ==== {{ku-tewîn-nav|nêr}} ==== Bi alfabeyên din ==== * {{ku-ar|مشک}} * {{ku-kr|мьшк}} ==== Formên alternatîv ==== * {{hw|ku|mihşk}} ==== Jê ==== {{kol3|ku | mişkane | mişke | mişko | mişkok }} ==== Bide ber ==== {{kol3|ku | meşk | misk | hişk | pişk | rişk }} ==== Bi zaravayên din ==== * {{Z|ckb}}: {{W-/ceribandin|ckb|mişk|sc=Latn}} * {{Z|sdh}}: {{W-/ceribandin|sdh|müş|sc=Latn}} * {{Z|lki}}: {{W-/ceribandin|lki|müş|sc=Latn}} * {{Z|hac}}: {{W-/ceribandin|hac|miłe|sc=Latn}} * {{Z|zza}}: {{W+/ceribandin|zza|merre}} ==== Werger ==== {{werger-ser/ceribandin|heywanê biçûk ê ku [[penîr]] dixwe|1}} * {{Z|ab}}: {{W-/ceribandin|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+/ceribandin|af|muis}} * {{Z|sq}}: {{W+/ceribandin|sq|mi|n}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} *: {{Z|gsw}}: {{W-/ceribandin|gsw|Muus|m}} * {{Z|am}}: {{W-/ceribandin|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-/ceribandin|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-/ceribandin|an|zorz}} * {{Z|rup}}: {{W-/ceribandin|rup|shoaric}} * {{Z|aer}}: {{W-/ceribandin|aer|thakwere}} * {{Z|as}}: {{W-/ceribandin|as|নিগনি}} * {{Z|ast}}: {{W+/ceribandin|ast|ratu|n}}, {{W+/ceribandin|ast|ratón|n}}, {{W+/ceribandin|ast|mur}} * {{Z|tay}}: {{W-/ceribandin|tay|olit}} * {{Z|av}}: {{W-/ceribandin|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+/ceribandin|ay|achaku}} * {{Z|ain}}: {{W-/ceribandin|ain|エㇾㇺ|tr=erm}}, {{W-/ceribandin|ain|エㇾム|tr=ermu}}, {{W-/ceribandin|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+/ceribandin|az|siçan}} * {{Z|ban}}: {{W-/ceribandin|ban|bikul}}, {{W-/ceribandin|ban|jero ketut}} * {{Z|eu}}: {{W+/ceribandin|eu|sagu}} * {{Z|ba}}: {{W-/ceribandin|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-/ceribandin|be|мыш|m}}, {{W-/ceribandin|be|мы́шка|m}} * {{Z|bal}}: {{W-/ceribandin|bal|مشک|tr=mušk}}, {{W-/ceribandin|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+/ceribandin|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+/ceribandin|br|logodenn|m}} * {{Z|bg}}: {{W+/ceribandin|bg|ми́шка|m}}, {{W-/ceribandin|bg|мишо́к|n}} * {{Z|my}}: {{W+/ceribandin|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-/ceribandin|pcc|ranz}}, {{W-/ceribandin|pcc|waauc}} * {{Z|ccc}}: {{W-/ceribandin|ccc|kijsili}} * {{Z|ce}}: {{W-/ceribandin|ce|дахка}} * {{Z|cs}}: {{W+/ceribandin|cs|myš|m}}, {{W+/ceribandin|cs|myšák|n}} * {{Z|cmn}}: {{W+|cmn|test}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin|amûra [[komputer]]ê|2|id=amûr}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} {{werger-bin/ceribandin}} jjwlpbbjo2bkfg0bbhplykfehpmzxp7 6794076 6794041 2026-10-14T20:35:36Z Wikihez 7344 Kurmancî/Navdêr: +mandarînî: [[test2]], tercime di bin zimanên xwe de, +çînî: [[test3]] (DefSideBoxes) 6794076 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} === Navdêr === [[Wêne:Micromys minutus (aka).jpg|thumb|Mişkek (1)]] [[Wêne:3-Tasten-Maus Microsoft.jpg|thumb|'''Mişkekî''' kompûterê (2)]] {{wp|Mişk (cudakirin)|Mişk}} {{navdêr|ku|z=n}} # {{f|ku|guhandar}} [[heywan|Heywanekî]] [[biçûk]] yê [[kirîner]] e û [[şibîn|dişibe]] [[cird]]an lê [[hûrik]]tir e. #: {{mk|ku|Kitik ne li mal e, navê '''mişkî''' Evdal e.}} # {{maneid|ku|informatîk}} {{f|ku|informatîk}} [[amûr|Amûrekî]] [[kompûter]]ê ye ku mirov pê [[nivîs]] û [[grafîk]]an [[livandin|dilivîne]]. ==== Tewandin ==== {{ku-tewîn-nav|nêr}} ==== Bi alfabeyên din ==== * {{ku-ar|مشک}} * {{ku-kr|мьшк}} ==== Formên alternatîv ==== * {{hw|ku|mihşk}} ==== Jê ==== {{kol3|ku | mişkane | mişke | mişko | mişkok }} ==== Bide ber ==== {{kol3|ku | meşk | misk | hişk | pişk | rişk }} ==== Bi zaravayên din ==== * {{Z|ckb}}: {{W-/ceribandin|ckb|mişk|sc=Latn}} * {{Z|sdh}}: {{W-/ceribandin|sdh|müş|sc=Latn}} * {{Z|lki}}: {{W-/ceribandin|lki|müş|sc=Latn}} * {{Z|hac}}: {{W-/ceribandin|hac|miłe|sc=Latn}} * {{Z|zza}}: {{W+/ceribandin|zza|merre}} ==== Werger ==== {{werger-ser/ceribandin|heywanê biçûk ê ku [[penîr]] dixwe|1}} * {{Z|ab}}: {{W-/ceribandin|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+/ceribandin|af|muis}} * {{Z|sq}}: {{W+/ceribandin|sq|mi|n}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} *: {{Z|gsw}}: {{W-/ceribandin|gsw|Muus|m}} * {{Z|am}}: {{W-/ceribandin|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-/ceribandin|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-/ceribandin|an|zorz}} * {{Z|rup}}: {{W-/ceribandin|rup|shoaric}} * {{Z|aer}}: {{W-/ceribandin|aer|thakwere}} * {{Z|as}}: {{W-/ceribandin|as|নিগনি}} * {{Z|ast}}: {{W+/ceribandin|ast|ratu|n}}, {{W+/ceribandin|ast|ratón|n}}, {{W+/ceribandin|ast|mur}} * {{Z|tay}}: {{W-/ceribandin|tay|olit}} * {{Z|av}}: {{W-/ceribandin|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+/ceribandin|ay|achaku}} * {{Z|ain}}: {{W-/ceribandin|ain|エㇾㇺ|tr=erm}}, {{W-/ceribandin|ain|エㇾム|tr=ermu}}, {{W-/ceribandin|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+/ceribandin|az|siçan}} * {{Z|ban}}: {{W-/ceribandin|ban|bikul}}, {{W-/ceribandin|ban|jero ketut}} * {{Z|eu}}: {{W+/ceribandin|eu|sagu}} * {{Z|ba}}: {{W-/ceribandin|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-/ceribandin|be|мыш|m}}, {{W-/ceribandin|be|мы́шка|m}} * {{Z|bal}}: {{W-/ceribandin|bal|مشک|tr=mušk}}, {{W-/ceribandin|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+/ceribandin|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+/ceribandin|br|logodenn|m}} * {{Z|bg}}: {{W+/ceribandin|bg|ми́шка|m}}, {{W-/ceribandin|bg|мишо́к|n}} * {{Z|my}}: {{W+/ceribandin|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-/ceribandin|pcc|ranz}}, {{W-/ceribandin|pcc|waauc}} * {{Z|ccc}}: {{W-/ceribandin|ccc|kijsili}} * {{Z|ce}}: {{W-/ceribandin|ce|дахка}} * {{Z|cs}}: {{W+/ceribandin|cs|myš|m}}, {{W+/ceribandin|cs|myšák|n}} * {{Z|zh}}: {{W|zh|test3}} *: {{Z|cmn}}: {{W+|cmn|test}}, {{W|cmn|test2}} {{werger-bin/ceribandin}} {{werger-ser/ceribandin|amûra [[komputer]]ê|2|id=amûr}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} {{werger-bin/ceribandin}} jnui7efhq2lyqm1qyw5l07zmc9dg1re 6794077 6794076 2026-10-14T20:50:45Z Wikihez 7344 /* Werger */ 6794077 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} === Navdêr === [[Wêne:Micromys minutus (aka).jpg|thumb|Mişkek (1)]] [[Wêne:3-Tasten-Maus Microsoft.jpg|thumb|'''Mişkekî''' kompûterê (2)]] {{wp|Mişk (cudakirin)|Mişk}} {{navdêr|ku|z=n}} # {{f|ku|guhandar}} [[heywan|Heywanekî]] [[biçûk]] yê [[kirîner]] e û [[şibîn|dişibe]] [[cird]]an lê [[hûrik]]tir e. #: {{mk|ku|Kitik ne li mal e, navê '''mişkî''' Evdal e.}} # {{maneid|ku|informatîk}} {{f|ku|informatîk}} [[amûr|Amûrekî]] [[kompûter]]ê ye ku mirov pê [[nivîs]] û [[grafîk]]an [[livandin|dilivîne]]. ==== Tewandin ==== {{ku-tewîn-nav|nêr}} ==== Bi alfabeyên din ==== * {{ku-ar|مشک}} * {{ku-kr|мьшк}} ==== Formên alternatîv ==== * {{hw|ku|mihşk}} ==== Jê ==== {{kol3|ku | mişkane | mişke | mişko | mişkok }} ==== Bide ber ==== {{kol3|ku | meşk | misk | hişk | pişk | rişk }} ==== Bi zaravayên din ==== * {{Z|ckb}}: {{W-/ceribandin|ckb|mişk|sc=Latn}} * {{Z|sdh}}: {{W-/ceribandin|sdh|müş|sc=Latn}} * {{Z|lki}}: {{W-/ceribandin|lki|müş|sc=Latn}} * {{Z|hac}}: {{W-/ceribandin|hac|miłe|sc=Latn}} * {{Z|zza}}: {{W+/ceribandin|zza|merre}} ==== Werger ==== {{werger-ser/ceribandin}} * {{Z|ab}}: {{W-/ceribandin|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+/ceribandin|af|muis}} * {{Z|sq}}: {{W+/ceribandin|sq|mi|n}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} *: {{Z|gsw}}: {{W-/ceribandin|gsw|Muus|m}} * {{Z|am}}: {{W-/ceribandin|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-/ceribandin|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-/ceribandin|an|zorz}} * {{Z|rup}}: {{W-/ceribandin|rup|shoaric}} * {{Z|aer}}: {{W-/ceribandin|aer|thakwere}} * {{Z|as}}: {{W-/ceribandin|as|নিগনি}} * {{Z|ast}}: {{W+/ceribandin|ast|ratu|n}}, {{W+/ceribandin|ast|ratón|n}}, {{W+/ceribandin|ast|mur}} * {{Z|tay}}: {{W-/ceribandin|tay|olit}} * {{Z|av}}: {{W-/ceribandin|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+/ceribandin|ay|achaku}} * {{Z|ain}}: {{W-/ceribandin|ain|エㇾㇺ|tr=erm}}, {{W-/ceribandin|ain|エㇾム|tr=ermu}}, {{W-/ceribandin|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+/ceribandin|az|siçan}} * {{Z|ban}}: {{W-/ceribandin|ban|bikul}}, {{W-/ceribandin|ban|jero ketut}} * {{Z|eu}}: {{W+/ceribandin|eu|sagu}} * {{Z|ba}}: {{W-/ceribandin|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-/ceribandin|be|мыш|m}}, {{W-/ceribandin|be|мы́шка|m}} * {{Z|bal}}: {{W-/ceribandin|bal|مشک|tr=mušk}}, {{W-/ceribandin|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+/ceribandin|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+/ceribandin|br|logodenn|m}} * {{Z|bg}}: {{W+/ceribandin|bg|ми́шка|m}}, {{W-/ceribandin|bg|мишо́к|n}} * {{Z|my}}: {{W+/ceribandin|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-/ceribandin|pcc|ranz}}, {{W-/ceribandin|pcc|waauc}} * {{Z|ccc}}: {{W-/ceribandin|ccc|kijsili}} * {{Z|ce}}: {{W-/ceribandin|ce|дахка}} * {{Z|cs}}: {{W+/ceribandin|cs|myš|m}}, {{W+/ceribandin|cs|myšák|n}} * {{Z|chr}}: {{W-/ceribandin|chr|ᏥᏍᏕᏥ}} * {{Z|ny}}: {{W-/ceribandin|ny|mbewa}} * {{Z|cic}}: {{W-/ceribandin|cic|pinti'}} * {{Z|zh}}: *: {{Z|yue}}: {{W-/ceribandin|yue|老鼠|tr=lou5 syu2}} *: {{Z|dng}}: {{W-/ceribandin|dng|лочў}} *: {{Z|hak}}: {{W-/ceribandin|hak|老鼠|tr=lo-chhú, ló-chhú, no-chhú}} *: {{Z|lzh}}: {{W-/ceribandin|lzh|鼠|tr=shǔ}}, {{W-/ceribandin|lzh|鼷|tr=xī}} *: {{Z|cmn}}: {{W+/ceribandin|cmn|老鼠|tr=lǎoshǔ}}, {{W+/ceribandin|cmn|鼠|tr=shǔ}}, {{W+/ceribandin|cmn|耗子|tr=hàozi}}, {{W+/ceribandin|cmn|小鼠|tr=xiǎoshǔ}}, {{W+/ceribandin|cmn|鼷|tr=xī}} *: {{Z|cdo}}: {{W-/ceribandin|cdo|老鼠|tr=lō̤-chṳ̄}} *: {{Z|nan}}: {{W+/ceribandin|nan|鳥鼠|tr=niáu-chhí, niáu-chhú}}, {{W+/ceribandin|nan|鼠|tr=chhí, chhú}}, {{W+/ceribandin|nan|錢鼠|tr=chîⁿ-chhí, chîⁿ-chhú}}, {{W-/ceribandin|nan|老鼠|tr=ló͘-chhír, ló͘-chhí, niáu-chhú, niáu-chhí, niáu-chhír}} *: {{Z|wuu}}: {{W-/ceribandin|wuu|老鼠}} * {{Z|ckt}}: {{W-/ceribandin|ckt|пипиӄыԓгын|tr=pipiqyḷgyn}} * {{Z|cv}}: {{W-/ceribandin|cv|шӑши|sc=Cyrl}} * {{Z|dlm}}: {{W-/ceribandin|dlm|surco|n}} * {{Z|da}}: {{W+/ceribandin|da|mus|g}} * {{Z|dv}}: {{W-/ceribandin|dv|މީދާ|sc=Thaa}} * {{Z|id}}: {{W+/ceribandin|id|tikus}} * {{Z|ar}}: {{W-/ceribandin|ar|فَأْرَة|m}}, {{W-/ceribandin|ar|فَأْر|n}} *: {{Z|arz}}: {{W-/ceribandin|arz|فار|tr=fār}} *: {{Z|ary}}: {{W-/ceribandin|ary|فار|tr=fār}} * {{Z|hy}}: {{W+/ceribandin|hy|մուկ}} *: {{Z|xcl}}: {{W-/ceribandin|xcl|մուկն|sc=Armn}} * {{Z|myv}}: {{W-/ceribandin|myv|чеерь}} * {{Z|eo}}: {{W+/ceribandin|eo|muso}}, {{W-/ceribandin|eo|musino}}, {{W-/ceribandin|eo|musido}}, {{W-/ceribandin|eo|musidino}} * {{Z|et}}: {{W+/ceribandin|et|hiir}} * {{Z|eve}}: {{W-/ceribandin|eve|ча̄ма̇кчан}} * {{Z|evn}}: {{W-/ceribandin|evn|сиңэрэкэн}} * {{Z|ee}}: {{W-/ceribandin|ee|afi|nt}} * {{Z|fa}}: {{W+/ceribandin|fa|موش|tr=muš}}, {{W+/ceribandin|fa|موشک|tr=mušk}} * {{Z|fo}}: {{W+/ceribandin|fo|mús|m}} * {{Z|fi}}: {{W+/ceribandin|fi|hiiri}} * {{Z|fr}}: {{W+/ceribandin|fr|souris|m}} * {{Z|fur}}: {{W-/ceribandin|fur|surîs|m}} * {{Z|frr}}: {{W-/ceribandin|frr|müs|nt}} * {{Z|fy}}: {{W-/ceribandin|fy|mûs}} * {{Z|gd}}: {{W-/ceribandin|gd|luch|m}} * {{Z|gl}}: {{W+/ceribandin|gl|rato|n}} * {{Z|gu}}: {{W-/ceribandin|gu|ઉંદર}} * {{Z|ka}}: {{W+/ceribandin|ka|თაგვი|sc=Geor}} * {{Z|haw}}: {{W-/ceribandin|haw|ʻiole liʻiliʻi}} * {{Z|ha}}: {{W-/ceribandin|ha|ɓera}} * {{Z|hi}}: {{W+/ceribandin|hi|चूहा|n|sc=Deva}} * {{Z|hil}}: {{W-/ceribandin|hil|ilaga}}, {{W-/ceribandin|hil|balabaw}} * {{Z|mww}}: {{W-/ceribandin|mww|nas}} * {{Z|nl}}: {{W+/ceribandin|nl|muis|m}} * {{Z|he}}: {{W+/ceribandin|he|עַכְבָּר|n|tr=akhbár|sc=Hebr}} * {{Z|io}}: {{W+/ceribandin|io|muso}} * {{Z|ig}}: {{W-/ceribandin|ig|ọke}} * {{Z|ilo}}: {{W-/ceribandin|ilo|bao}} * {{Z|ang}}: {{W-/ceribandin|ang|mūs}} * {{Z|en}}: {{W+/ceribandin|en|mouse}} * {{Z|inh}}: {{W-/ceribandin|inh|дахка}} * {{Z|ia}}: {{W-/ceribandin|ia|mus}}, {{W-/ceribandin|ia|mure}} * {{Z|ga}}: {{W+/ceribandin|ga|luch|m}}, {{W+/ceribandin|ga|luchóg|m}} * {{Z|it}}: {{W+/ceribandin|it|topo|n}}, {{W+/ceribandin|it|sorcio|n}}, {{W+/ceribandin|it|ratto|n}} * {{Z|is}}: {{W+/ceribandin|is|mús|m}} * {{Z|ja}}: {{W+/ceribandin|ja|鼠|tr=ねずみ, nezumi}}, {{W+/ceribandin|ja|ネズミ|tr=nezumi}}, {{W+/ceribandin|ja|ハツカネズミ|tr=hatsukanezumi|sc=Jpan}}, {{W+/ceribandin|ja|マウス|tr=mausu|sc=Jpan}} * {{Z|jra}}: {{W-/ceribandin|jra|tơkuih}} * {{Z|jv}}: {{W+/ceribandin|jv|tikus}} * {{Z|kl}}: {{W-/ceribandin|kl|teriaq}} * {{Z|xal}}: {{W-/ceribandin|xal|хулһн|sc=Cyrl}} * {{Z|kn}}: {{W+/ceribandin|kn|ಇಲಿ|sc=Knda}} * {{Z|kr}}: {{W-/ceribandin|kr|jilwa}} * {{Z|pam}}: {{W-/ceribandin|pam|dagis}} * {{Z|krl}}: {{W-/ceribandin|krl|hiiri}} * {{Z|csb}}: {{W-/ceribandin|csb|mësz|m}} * {{Z|ca}}: {{W+/ceribandin|ca|ratolí|n}} * {{Z|qu}}: {{W-/ceribandin|qu|ukuca}}, {{W-/ceribandin|qu|ukuş}}, {{W-/ceribandin|qu|juk'ucha}} * {{Z|ky}}: {{W+/ceribandin|ky|чычкан|sc=Cyrl}} * {{Z|si}}: {{W-/ceribandin|si|මීයා|sc=Sinh}} * {{Z|rw}}: {{W-/ceribandin|rw|umushushwe|c3/4}}, {{W-/ceribandin|rw|imbeba|c9/10}}, {{W-/ceribandin|rw|im-beba}} * {{Z|ko}}: {{W+/ceribandin|ko|생쥐}}, {{W+/ceribandin|ko|쥐}} * {{Z|kw}}: {{W-/ceribandin|kw|logosen|m}} * {{Z|rop}}: {{W-/ceribandin|rop|brudbrud}} * {{Z|xug}}: {{W-/ceribandin|xug|っゑっちゅー|tr=wwecchū}} * {{Z|fkv}}: {{W-/ceribandin|fkv|hiiri}} * {{Z|ltg}}: {{W-/ceribandin|ltg|pele}} * {{Z|la}}: {{W+/ceribandin|la|mūs|n|m}} * {{Z|lv}}: {{W+/ceribandin|lv|pele|m}} * {{Z|lo}}: {{W+/ceribandin|lo|ໜູ}} * {{Z|lez}}: {{W-/ceribandin|lez|кьиф}} * {{Z|lt}}: {{W+/ceribandin|lt|pelė|m}}, {{W-/ceribandin|lt|pelėnas|n}} * {{Z|lmo}}: {{W-/ceribandin|lmo|ratt}} * {{Z|khb}}: {{W-/ceribandin|khb|ᦐᦴ}} * {{Z|lg}}: {{W-/ceribandin|lg|emmese}} * {{Z|lb}}: {{W+/ceribandin|lb|Maus|m}} * {{Z|lut}}: {{W-/ceribandin|lut|k'ʷat'ad}} * {{Z|mk}}: {{W+/ceribandin|mk|глушец|n}}, {{W-/ceribandin|mk|глувче|nt}}, {{W-/ceribandin|mk|миш|n}}, {{W+/ceribandin|mk|глушец|n}} * {{Z|ml}}: {{W+/ceribandin|ml|ചുണ്ടെലി|sc=Mlym}} * {{Z|ms}}: {{W+/ceribandin|ms|tikus}}, {{W-/ceribandin|ms|mencit}} * {{Z|mt}}: {{W-/ceribandin|mt|ġurdien|n}} * {{Z|mnc}}: {{W-/ceribandin|mnc|ᠰᡳᠩᡤᡝᡵᡳ}} * {{Z|gv}}: {{W-/ceribandin|gv|lugh|n}} * {{Z|mi}}: {{W+/ceribandin|mi|kiore}} * {{Z|mr}}: {{W-/ceribandin|mr|उंन्दिर|sc=Deva}} * Marî: *: {{Z|mhr}}: {{W-/ceribandin|mhr|коля}} *: {{Z|mrj}}: {{W-/ceribandin|mrj|каля}} * {{Z|mzn}}: {{W-/ceribandin|mzn|گل|tr=gal}} * {{Z|hu}}: {{W+/ceribandin|hu|egér}} * {{Z|egy}}: {{W-egy|pnw|h=p:n-nw-w-F27}} * {{Z|mvi}}: {{W-/ceribandin|mvi|ゆむぬ|tr=yumunu}} * {{Z|mic}}: {{W-/ceribandin|mic|apigji'j|an}}, {{W-/ceribandin|mic|apugji'j|an}} * {{Z|mwl}}: {{W-/ceribandin|mwl|rato|n}} * {{Z|mdf}}: {{W-/ceribandin|mdf|шеер}} * {{Z|mn}}: {{W+/ceribandin|mn|хулгана|sc=Cyrl}}, {{W+/ceribandin|mn|оготно|sc=Cyrl}} * {{Z|nah}}: {{W+/ceribandin|nah|quimichin}} * {{Z|gld}}: {{W-/ceribandin|gld|сингэрэ}} * {{Z|nap}}: {{W-/ceribandin|nap|sorece|n}} * {{Z|nv}}: {{W-/ceribandin|nv|naʼatsʼǫǫsí}} * {{Z|nds}}: *: {{Z|nds-de}}: {{W-/ceribandin|nds-de|Muus|m}} * {{Z|ne}}: {{W+/ceribandin|ne|मुसो|sc=Deva}} * {{Z|niv}}: {{W-/ceribandin|niv|мухтук|sc=Cyrl}} * {{Z|nrf}}: {{W-/ceribandin|nrf|souothis|m}} * {{Z|no}}: {{W+/ceribandin|no|mus|m}} * {{Z|oj}}: {{W-/ceribandin|oj|waawaabiganoojiinh}} * {{Z|ryu}}: {{W-/ceribandin|ryu|ゑんちゅ|tr=wenchu}} * {{Z|oc}}: {{W+/ceribandin|oc|mirga|m}}, {{W-/ceribandin|oc|fureta|m}}, {{W+/ceribandin|oc|ratuga|m}} * {{Z|or}}: {{W+/ceribandin|or|ମୂଷା}} * {{Z|osa}}: {{W-/ceribandin|osa|įchóka}} * {{Z|os}}: {{W-/ceribandin|os|мыст|sc=Cyrl}} * {{Z|ota}}: {{W-/ceribandin|ota|صیچان|tr=sıçan|sc=ota-Arab}}, {{W-/ceribandin|ota|فأرة|tr=fare|sc=ota-Arab}}, {{W-/ceribandin|ota|فأر|tr=far|sc=ota-Arab}}, {{W-/ceribandin|ota|موش|tr=muş}} * {{Z|ug}}: {{W-/ceribandin|ug|چاشقان|sc=ug-Arab}} * {{Z|uz}}: {{W+/ceribandin|uz|sichqon}} * {{Z|ps}}: {{W+/ceribandin|ps|موږک|n|tr=muģák|sc=ps-Arab}} * {{Z|pdt}}: {{W-/ceribandin|pdt|Mus|m}} * {{Z|pl}}: {{W+/ceribandin|pl|mysz|m}}, {{W+/ceribandin|pl|myszka|m}} * {{Z|pt}}: {{W+/ceribandin|pt|rato|n}}, {{W+/ceribandin|pt|camundongo|n}} * {{Z|prg}}: {{W-/ceribandin|prg|pelē}} * {{Z|pa}}: {{W+/ceribandin|pa|ਚੂਹਾ}} * {{Z|kk}}: {{W-/ceribandin|kk|тышқан|sc=Cyrl}} * {{Z|rm}}: {{W-/ceribandin|rm|mieur}} * {{Z|ro}}: {{W+/ceribandin|ro|șoarece|n}} * {{Z|ru}}: {{W+/ceribandin|ru|мышь|m}}, {{W+/ceribandin|ru|мы́шка|m}} * {{Z|ksw}}: {{W-/ceribandin|ksw|ယုၢ်}} * {{Z|skb}}: {{W-/ceribandin|skb|หนู่}} * {{Z|se}}: {{W-/ceribandin|se|sáhpán}} * {{Z|smn}}: {{W-/ceribandin|smn|säplig}} * {{Z|sms}}: {{W-/ceribandin|sms|sä´ppli}} * {{Z|sm}}: {{W-/ceribandin|sm|'isumu}}, {{W-/ceribandin|sm|'iole}} * {{Z|sa}}: {{W+/ceribandin|sa|मूष्|n|m}}, {{W+/ceribandin|sa|आखु|n}} * {{Z|sc}}: {{W-/ceribandin|sc|tope}}, {{W-/ceribandin|sc|topi}} *: {{Z|sc}}: {{W-/ceribandin|sc|medrona|m}}, {{W-/ceribandin|sc|topi}}, {{W-/ceribandin|sc|sórixi|n}} * {{Z|ceb}}: {{W-/ceribandin|ceb|ilaga}} * {{Z|scn}}: {{W+/ceribandin|scn|surci|n}}, {{W+/ceribandin|scn|sùrici|n}} * {{Z|sd}}: {{W-/ceribandin|sd|ڪوئو|tr=kūʾō|cuda=ڪوُئو|sc=sd-Arab}} * {{Z|sh}}: *: Kirîlî: {{W-/ceribandin|sh|ми̏ш|n}} *: Latînî: {{W+/ceribandin|sh|mȉš|n}} * {{Z|sco}}: {{W-/ceribandin|sco|moose}} * {{Z|cu}}: *: Kirîlî: {{W-/ceribandin|cu|мꙑшь|m}} * {{Z|orv}}: {{W-/ceribandin|orv|мꙑшь|m}} * {{Z|sk}}: {{W+/ceribandin|sk|myš|m}} * {{Z|sl}}: {{W+/ceribandin|sl|miš|m}} * {{Z|so}}: {{W+/ceribandin|so|jiir|n}} * Sorbî: *: {{Z|hsb}}: {{W-/ceribandin|hsb|myš|m}}, {{W-/ceribandin|hsb|myška|m}} *: {{Z|dsb}}: {{W-/ceribandin|dsb|myš|m}}, {{W-/ceribandin|dsb|myška|}} * {{Z|st}}: {{W+/ceribandin|st|tweba}} * {{Z|es}}: {{W+/ceribandin|es|ratón|n}}, {{W+/ceribandin|es|mur|n}} * {{Z|sw}}: {{W+/ceribandin|sw|kipanya}} * {{Z|sv}}: {{W+/ceribandin|sv|mus|g}} * {{Z|shn}}: {{W-/ceribandin|shn|ၼူ}} * {{Z|tg}}: {{W+/ceribandin|tg|муш|sc=Cyrl}} * {{Z|tl}}: {{W+/ceribandin|tl|daga}} * {{Z|tht}}: {{W-/ceribandin|tht|dlūne}} * {{Z|tly}}: {{W-/ceribandin|tly|морә|tr=morǝ}} * {{Z|ta}}: {{W+/ceribandin|ta|சுண்டெலி|sc=Taml}} * {{Z|twf}}: {{W-/ceribandin|twf|cìyúna}} * {{Z|roa-tar}}: {{W-/ceribandin|roa-tar|topo|n}} * {{Z|rif}}: {{W-/ceribandin|rif|aɣerḍa|n}} * {{Z|blt}}: {{W-/ceribandin|blt|ꪘꪴ}} * {{Z|nod}}: {{W-/ceribandin|nod|ᩉ᩠ᨶᩪ}} * {{Z|th}}: {{W+/ceribandin|th|หนู}} * {{Z|te}}: {{W+/ceribandin|te|ఎలుక|sc=Telu}} * {{Z|crh}}: {{W-/ceribandin|crh|sıçan}} * {{Z|tt}}: {{W+/ceribandin|tt|тычкан|sc=Cyrl}} * {{Z|ti}}: {{W-/ceribandin|ti|ኣንጭዋ}} * {{Z|tr}}: {{W+/ceribandin|tr|fare}}, {{W+/ceribandin|tr|sıçan}} * {{Z|tk}}: {{W-/ceribandin|tk|syçan}}, {{W-/ceribandin|tk|sıçan}} * {{Z|bo}}: {{W-/ceribandin|bo|ཙི་ཙི|sc=Tibt}} * {{Z|tli}}: {{W-/ceribandin|tli|kagaák}} * {{Z|tpi}}: {{W-/ceribandin|tpi|liklik rat}} * {{Z|to}}: {{W-/ceribandin|to|kumā}} * {{Z|tn}}: {{W-/ceribandin|tn|pêba}} * {{Z|tum}}: {{W-/ceribandin|tum|mbewa}} * {{Z|udm}}: {{W-/ceribandin|udm|шыр}} * {{Z|ur}}: {{W-/ceribandin|ur|چوہا|n|tr=cūhā|sc=ur-Arab}} * {{Z|uk}}: {{W+/ceribandin|uk|ми́ша|m}}, {{W-/ceribandin|uk|миш|m}}, {{W-/ceribandin|uk|ми́шка|m}}, {{W-/ceribandin|uk|миша́к|n}} * {{Z|vec}}: {{W-/ceribandin|vec|ràto}}, {{W-/ceribandin|vec|sorze}} * {{Z|vi}}: {{W+/ceribandin|vi|chuột}} * {{Z|vo}}: {{W+/ceribandin|vo|mug}} * {{Z|wa}}: {{W+/ceribandin|wa|sori|m}} * {{Z|cy}}: {{W+/ceribandin|cy|llygoden|m}}, {{W+/ceribandin|cy|llygod|p}} * {{Z|wym}}: {{W-/ceribandin|wym|maojs}} * {{Z|wo}}: {{W-/ceribandin|wo|janax|cuda=janax ji}} * {{Z|km}}: {{W-/ceribandin|km|កណ្ដុរស្រូវ}}, {{W+/ceribandin|km|កណ្ដុរ}} * Kipchak: *: {{Z|qwm}}: {{W-/ceribandin|qwm|սըչխան|tr=sïč̣χan}} * {{Z|xh}}: {{W-/ceribandin|xh|ilifu}} * {{Z|rys}}: {{W-/ceribandin|rys|うぇんちゅ|tr=Wenchu}} * {{Z|yai}}: {{W-/ceribandin|yai|муш}} * {{Z|sah}}: {{W-/ceribandin|sah|кутуйах|sc=Cyrl}} * {{Z|yag}}: {{W-/ceribandin|yag|wasána}} * {{Z|yi}}: {{W-/ceribandin|yi|מויז|sc=Hebr}} * {{Z|yoi}}: {{W-/ceribandin|yoi|うやんとぅ|tr=uyantu}} * {{Z|yo}}: {{W-/ceribandin|yo|èkúté}}, {{W-/ceribandin|yo|eliri}} * {{Z|el}}: {{W+/ceribandin|el|ποντίκι|nt}}, {{W+/ceribandin|el|ποντικός|n|sc=Grek}} *: {{Z|grc}}: {{W-/ceribandin|grc|μῦς|n}}, {{W-/ceribandin|grc|ποντικός|n}} * {{Z|za}}: {{W-/ceribandin|za|nou}} * {{Z|zu}}: {{W+/ceribandin|zu|igundane|c5|c6}} {{werger-bin/ceribandin}} :{{werger çavkanî|en|mouse|oldid=75626351|beş=Ev qismê Wergerê}} kdd6jaw9ldi19gl74r3gnmqto4usw0u 6794087 6794077 2026-10-14T21:49:32Z Wikihez 7344 Kurmancî/Navdêr: +mandarînî: [[test]], ±Mandarînî (DefSideBoxes) 6794087 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} === Navdêr === [[Wêne:Micromys minutus (aka).jpg|thumb|Mişkek (1)]] [[Wêne:3-Tasten-Maus Microsoft.jpg|thumb|'''Mişkekî''' kompûterê (2)]] {{wp|Mişk (cudakirin)|Mişk}} {{navdêr|ku|z=n}} # {{f|ku|guhandar}} [[heywan|Heywanekî]] [[biçûk]] yê [[kirîner]] e û [[şibîn|dişibe]] [[cird]]an lê [[hûrik]]tir e. #: {{mk|ku|Kitik ne li mal e, navê '''mişkî''' Evdal e.}} # {{maneid|ku|informatîk}} {{f|ku|informatîk}} [[amûr|Amûrekî]] [[kompûter]]ê ye ku mirov pê [[nivîs]] û [[grafîk]]an [[livandin|dilivîne]]. ==== Tewandin ==== {{ku-tewîn-nav|nêr}} ==== Bi alfabeyên din ==== * {{ku-ar|مشک}} * {{ku-kr|мьшк}} ==== Formên alternatîv ==== * {{hw|ku|mihşk}} ==== Jê ==== {{kol3|ku | mişkane | mişke | mişko | mişkok }} ==== Bide ber ==== {{kol3|ku | meşk | misk | hişk | pişk | rişk }} ==== Bi zaravayên din ==== * {{Z|ckb}}: {{W-/ceribandin|ckb|mişk|sc=Latn}} * {{Z|sdh}}: {{W-/ceribandin|sdh|müş|sc=Latn}} * {{Z|lki}}: {{W-/ceribandin|lki|müş|sc=Latn}} * {{Z|hac}}: {{W-/ceribandin|hac|miłe|sc=Latn}} * {{Z|zza}}: {{W+/ceribandin|zza|merre}} ==== Werger ==== {{werger-ser/ceribandin}} * {{Z|ab}}: {{W-/ceribandin|ab|аҳәынаҧ|sc=Cyrl}} * {{Z|af}}: {{W+/ceribandin|af|muis}} * {{Z|sq}}: {{W+/ceribandin|sq|mi|n}} * {{Z|de}}: {{W+/ceribandin|de|Maus|m}} *: {{Z|gsw}}: {{W-/ceribandin|gsw|Muus|m}} * {{Z|am}}: {{W-/ceribandin|am|አይጥ|sc=Ethi}} * {{Z|ani}}: {{W-/ceribandin|ani|гьинкӏкӏу}} * {{Z|an}}: {{W-/ceribandin|an|zorz}} * {{Z|rup}}: {{W-/ceribandin|rup|shoaric}} * {{Z|aer}}: {{W-/ceribandin|aer|thakwere}} * {{Z|as}}: {{W-/ceribandin|as|নিগনি}} * {{Z|ast}}: {{W+/ceribandin|ast|ratu|n}}, {{W+/ceribandin|ast|ratón|n}}, {{W+/ceribandin|ast|mur}} * {{Z|tay}}: {{W-/ceribandin|tay|olit}} * {{Z|av}}: {{W-/ceribandin|av|гӏункӏкӏ|sc=Cyrl}} * {{Z|ay}}: {{W+/ceribandin|ay|achaku}} * {{Z|ain}}: {{W-/ceribandin|ain|エㇾㇺ|tr=erm}}, {{W-/ceribandin|ain|エㇾム|tr=ermu}}, {{W-/ceribandin|ain|エルㇺ|tr=erum}} * {{Z|az}}: {{W+/ceribandin|az|siçan}} * {{Z|ban}}: {{W-/ceribandin|ban|bikul}}, {{W-/ceribandin|ban|jero ketut}} * {{Z|eu}}: {{W+/ceribandin|eu|sagu}} * {{Z|ba}}: {{W-/ceribandin|ba|сысҡан|sc=Cyrl}} * {{Z|be}}: {{W-/ceribandin|be|мыш|m}}, {{W-/ceribandin|be|мы́шка|m}} * {{Z|bal}}: {{W-/ceribandin|bal|مشک|tr=mušk}}, {{W-/ceribandin|bal|موشک|tr=múšk}} * {{Z|bn}}: {{W+/ceribandin|bn|ইঁদুর|sc=Beng}} * {{Z|br}}: {{W+/ceribandin|br|logodenn|m}} * {{Z|bg}}: {{W+/ceribandin|bg|ми́шка|m}}, {{W-/ceribandin|bg|мишо́к|n}} * {{Z|my}}: {{W+/ceribandin|my|ကြွက်|sc=Mymr}} * {{Z|pcc}}: {{W-/ceribandin|pcc|ranz}}, {{W-/ceribandin|pcc|waauc}} * {{Z|ccc}}: {{W-/ceribandin|ccc|kijsili}} * {{Z|ce}}: {{W-/ceribandin|ce|дахка}} * {{Z|cs}}: {{W+/ceribandin|cs|myš|m}}, {{W+/ceribandin|cs|myšák|n}} * {{Z|chr}}: {{W-/ceribandin|chr|ᏥᏍᏕᏥ}} * {{Z|ny}}: {{W-/ceribandin|ny|mbewa}} * {{Z|cic}}: {{W-/ceribandin|cic|pinti'}} * {{Z|zh}}: *: {{Z|yue}}: {{W-/ceribandin|yue|老鼠|tr=lou5 syu2}} *: {{Z|dng}}: {{W-/ceribandin|dng|лочў}} *: {{Z|hak}}: {{W-/ceribandin|hak|老鼠|tr=lo-chhú, ló-chhú, no-chhú}} *: {{Z|lzh}}: {{W-/ceribandin|lzh|鼠|tr=shǔ}}, {{W-/ceribandin|lzh|鼷|tr=xī}} *: {{Z|cmn}}: {{W+/ceribandin|cmn|老鼠|tr=lǎoshǔ}}, {{W+/ceribandin|cmn|鼠|tr=shǔ}}, {{W+/ceribandin|cmn|耗子|tr=hàozi}}, {{W+/ceribandin|cmn|小鼠|tr=xiǎoshǔ}}, {{W+/ceribandin|cmn|鼷|tr=xī|guman=erê}}, {{W+|cmn|test}} *: {{Z|cdo}}: {{W-/ceribandin|cdo|老鼠|tr=lō̤-chṳ̄}} *: {{Z|nan}}: {{W+/ceribandin|nan|鳥鼠|tr=niáu-chhí, niáu-chhú}}, {{W+/ceribandin|nan|鼠|tr=chhí, chhú}}, {{W+/ceribandin|nan|錢鼠|tr=chîⁿ-chhí, chîⁿ-chhú}}, {{W-/ceribandin|nan|老鼠|tr=ló͘-chhír, ló͘-chhí, niáu-chhú, niáu-chhí, niáu-chhír}} *: {{Z|wuu}}: {{W-/ceribandin|wuu|老鼠}} * {{Z|ckt}}: {{W-/ceribandin|ckt|пипиӄыԓгын|tr=pipiqyḷgyn}} * {{Z|cv}}: {{W-/ceribandin|cv|шӑши|sc=Cyrl}} * {{Z|dlm}}: {{W-/ceribandin|dlm|surco|n}} * {{Z|da}}: {{W+/ceribandin|da|mus|g}} * {{Z|dv}}: {{W-/ceribandin|dv|މީދާ|sc=Thaa}} * {{Z|id}}: {{W+/ceribandin|id|tikus}} * {{Z|ar}}: {{W-/ceribandin|ar|فَأْرَة|m}}, {{W-/ceribandin|ar|فَأْر|n}} *: {{Z|arz}}: {{W-/ceribandin|arz|فار|tr=fār}} *: {{Z|ary}}: {{W-/ceribandin|ary|فار|tr=fār}} * {{Z|hy}}: {{W+/ceribandin|hy|մուկ}} *: {{Z|xcl}}: {{W-/ceribandin|xcl|մուկն|sc=Armn}} * {{Z|myv}}: {{W-/ceribandin|myv|чеерь}} * {{Z|eo}}: {{W+/ceribandin|eo|muso}}, {{W-/ceribandin|eo|musino}}, {{W-/ceribandin|eo|musido}}, {{W-/ceribandin|eo|musidino}} * {{Z|et}}: {{W+/ceribandin|et|hiir}} * {{Z|eve}}: {{W-/ceribandin|eve|ча̄ма̇кчан}} * {{Z|evn}}: {{W-/ceribandin|evn|сиңэрэкэн}} * {{Z|ee}}: {{W-/ceribandin|ee|afi|nt}} * {{Z|fa}}: {{W+/ceribandin|fa|موش|tr=muš}}, {{W+/ceribandin|fa|موشک|tr=mušk}} * {{Z|fo}}: {{W+/ceribandin|fo|mús|m}} * {{Z|fi}}: {{W+/ceribandin|fi|hiiri}} * {{Z|fr}}: {{W+/ceribandin|fr|souris|m}} * {{Z|fur}}: {{W-/ceribandin|fur|surîs|m}} * {{Z|frr}}: {{W-/ceribandin|frr|müs|nt}} * {{Z|fy}}: {{W-/ceribandin|fy|mûs}} * {{Z|gd}}: {{W-/ceribandin|gd|luch|m}} * {{Z|gl}}: {{W+/ceribandin|gl|rato|n}} * {{Z|gu}}: {{W-/ceribandin|gu|ઉંદર}} * {{Z|ka}}: {{W+/ceribandin|ka|თაგვი|sc=Geor}} * {{Z|haw}}: {{W-/ceribandin|haw|ʻiole liʻiliʻi}} * {{Z|ha}}: {{W-/ceribandin|ha|ɓera}} * {{Z|hi}}: {{W+/ceribandin|hi|चूहा|n|sc=Deva}} * {{Z|hil}}: {{W-/ceribandin|hil|ilaga}}, {{W-/ceribandin|hil|balabaw}} * {{Z|mww}}: {{W-/ceribandin|mww|nas}} * {{Z|nl}}: {{W+/ceribandin|nl|muis|m}} * {{Z|he}}: {{W+/ceribandin|he|עַכְבָּר|n|tr=akhbár|sc=Hebr}} * {{Z|io}}: {{W+/ceribandin|io|muso}} * {{Z|ig}}: {{W-/ceribandin|ig|ọke}} * {{Z|ilo}}: {{W-/ceribandin|ilo|bao}} * {{Z|ang}}: {{W-/ceribandin|ang|mūs}} * {{Z|en}}: {{W+/ceribandin|en|mouse}} * {{Z|inh}}: {{W-/ceribandin|inh|дахка}} * {{Z|ia}}: {{W-/ceribandin|ia|mus}}, {{W-/ceribandin|ia|mure}} * {{Z|ga}}: {{W+/ceribandin|ga|luch|m}}, {{W+/ceribandin|ga|luchóg|m}} * {{Z|it}}: {{W+/ceribandin|it|topo|n}}, {{W+/ceribandin|it|sorcio|n}}, {{W+/ceribandin|it|ratto|n}} * {{Z|is}}: {{W+/ceribandin|is|mús|m}} * {{Z|ja}}: {{W+/ceribandin|ja|鼠|tr=ねずみ, nezumi}}, {{W+/ceribandin|ja|ネズミ|tr=nezumi}}, {{W+/ceribandin|ja|ハツカネズミ|tr=hatsukanezumi|sc=Jpan}}, {{W+/ceribandin|ja|マウス|tr=mausu|sc=Jpan}} * {{Z|jra}}: {{W-/ceribandin|jra|tơkuih}} * {{Z|jv}}: {{W+/ceribandin|jv|tikus}} * {{Z|kl}}: {{W-/ceribandin|kl|teriaq}} * {{Z|xal}}: {{W-/ceribandin|xal|хулһн|sc=Cyrl}} * {{Z|kn}}: {{W+/ceribandin|kn|ಇಲಿ|sc=Knda}} * {{Z|kr}}: {{W-/ceribandin|kr|jilwa}} * {{Z|pam}}: {{W-/ceribandin|pam|dagis}} * {{Z|krl}}: {{W-/ceribandin|krl|hiiri}} * {{Z|csb}}: {{W-/ceribandin|csb|mësz|m}} * {{Z|ca}}: {{W+/ceribandin|ca|ratolí|n}} * {{Z|qu}}: {{W-/ceribandin|qu|ukuca}}, {{W-/ceribandin|qu|ukuş}}, {{W-/ceribandin|qu|juk'ucha}} * {{Z|ky}}: {{W+/ceribandin|ky|чычкан|sc=Cyrl}} * {{Z|si}}: {{W-/ceribandin|si|මීයා|sc=Sinh}} * {{Z|rw}}: {{W-/ceribandin|rw|umushushwe|c3/4}}, {{W-/ceribandin|rw|imbeba|c9/10}}, {{W-/ceribandin|rw|im-beba}} * {{Z|ko}}: {{W+/ceribandin|ko|생쥐}}, {{W+/ceribandin|ko|쥐}} * {{Z|kw}}: {{W-/ceribandin|kw|logosen|m}} * {{Z|rop}}: {{W-/ceribandin|rop|brudbrud}} * {{Z|xug}}: {{W-/ceribandin|xug|っゑっちゅー|tr=wwecchū}} * {{Z|fkv}}: {{W-/ceribandin|fkv|hiiri}} * {{Z|ltg}}: {{W-/ceribandin|ltg|pele}} * {{Z|la}}: {{W+/ceribandin|la|mūs|n|m}} * {{Z|lv}}: {{W+/ceribandin|lv|pele|m}} * {{Z|lo}}: {{W+/ceribandin|lo|ໜູ}} * {{Z|lez}}: {{W-/ceribandin|lez|кьиф}} * {{Z|lt}}: {{W+/ceribandin|lt|pelė|m}}, {{W-/ceribandin|lt|pelėnas|n}} * {{Z|lmo}}: {{W-/ceribandin|lmo|ratt}} * {{Z|khb}}: {{W-/ceribandin|khb|ᦐᦴ}} * {{Z|lg}}: {{W-/ceribandin|lg|emmese}} * {{Z|lb}}: {{W+/ceribandin|lb|Maus|m}} * {{Z|lut}}: {{W-/ceribandin|lut|k'ʷat'ad}} * {{Z|mk}}: {{W+/ceribandin|mk|глушец|n}}, {{W-/ceribandin|mk|глувче|nt}}, {{W-/ceribandin|mk|миш|n}}, {{W+/ceribandin|mk|глушец|n}} * {{Z|ml}}: {{W+/ceribandin|ml|ചുണ്ടെലി|sc=Mlym}} * {{Z|ms}}: {{W+/ceribandin|ms|tikus}}, {{W-/ceribandin|ms|mencit}} * {{Z|mt}}: {{W-/ceribandin|mt|ġurdien|n}} * {{Z|mnc}}: {{W-/ceribandin|mnc|ᠰᡳᠩᡤᡝᡵᡳ}} * {{Z|gv}}: {{W-/ceribandin|gv|lugh|n}} * {{Z|mi}}: {{W+/ceribandin|mi|kiore}} * {{Z|mr}}: {{W-/ceribandin|mr|उंन्दिर|sc=Deva}} * Marî: *: {{Z|mhr}}: {{W-/ceribandin|mhr|коля}} *: {{Z|mrj}}: {{W-/ceribandin|mrj|каля}} * {{Z|mzn}}: {{W-/ceribandin|mzn|گل|tr=gal}} * {{Z|hu}}: {{W+/ceribandin|hu|egér}} * {{Z|egy}}: {{W-egy|pnw|h=p:n-nw-w-F27}} * {{Z|mvi}}: {{W-/ceribandin|mvi|ゆむぬ|tr=yumunu}} * {{Z|mic}}: {{W-/ceribandin|mic|apigji'j|an}}, {{W-/ceribandin|mic|apugji'j|an}} * {{Z|mwl}}: {{W-/ceribandin|mwl|rato|n}} * {{Z|mdf}}: {{W-/ceribandin|mdf|шеер}} * {{Z|mn}}: {{W+/ceribandin|mn|хулгана|sc=Cyrl}}, {{W+/ceribandin|mn|оготно|sc=Cyrl}} * {{Z|nah}}: {{W+/ceribandin|nah|quimichin}} * {{Z|gld}}: {{W-/ceribandin|gld|сингэрэ}} * {{Z|nap}}: {{W-/ceribandin|nap|sorece|n}} * {{Z|nv}}: {{W-/ceribandin|nv|naʼatsʼǫǫsí}} * {{Z|nds}}: *: {{Z|nds-de}}: {{W-/ceribandin|nds-de|Muus|m}} * {{Z|ne}}: {{W+/ceribandin|ne|मुसो|sc=Deva}} * {{Z|niv}}: {{W-/ceribandin|niv|мухтук|sc=Cyrl}} * {{Z|nrf}}: {{W-/ceribandin|nrf|souothis|m}} * {{Z|no}}: {{W+/ceribandin|no|mus|m}} * {{Z|oj}}: {{W-/ceribandin|oj|waawaabiganoojiinh}} * {{Z|ryu}}: {{W-/ceribandin|ryu|ゑんちゅ|tr=wenchu}} * {{Z|oc}}: {{W+/ceribandin|oc|mirga|m}}, {{W-/ceribandin|oc|fureta|m}}, {{W+/ceribandin|oc|ratuga|m}} * {{Z|or}}: {{W+/ceribandin|or|ମୂଷା}} * {{Z|osa}}: {{W-/ceribandin|osa|įchóka}} * {{Z|os}}: {{W-/ceribandin|os|мыст|sc=Cyrl}} * {{Z|ota}}: {{W-/ceribandin|ota|صیچان|tr=sıçan|sc=ota-Arab}}, {{W-/ceribandin|ota|فأرة|tr=fare|sc=ota-Arab}}, {{W-/ceribandin|ota|فأر|tr=far|sc=ota-Arab}}, {{W-/ceribandin|ota|موش|tr=muş}} * {{Z|ug}}: {{W-/ceribandin|ug|چاشقان|sc=ug-Arab}} * {{Z|uz}}: {{W+/ceribandin|uz|sichqon}} * {{Z|ps}}: {{W+/ceribandin|ps|موږک|n|tr=muģák|sc=ps-Arab}} * {{Z|pdt}}: {{W-/ceribandin|pdt|Mus|m}} * {{Z|pl}}: {{W+/ceribandin|pl|mysz|m}}, {{W+/ceribandin|pl|myszka|m}} * {{Z|pt}}: {{W+/ceribandin|pt|rato|n}}, {{W+/ceribandin|pt|camundongo|n}} * {{Z|prg}}: {{W-/ceribandin|prg|pelē}} * {{Z|pa}}: {{W+/ceribandin|pa|ਚੂਹਾ}} * {{Z|kk}}: {{W-/ceribandin|kk|тышқан|sc=Cyrl}} * {{Z|rm}}: {{W-/ceribandin|rm|mieur}} * {{Z|ro}}: {{W+/ceribandin|ro|șoarece|n}} * {{Z|ru}}: {{W+/ceribandin|ru|мышь|m}}, {{W+/ceribandin|ru|мы́шка|m}} * {{Z|ksw}}: {{W-/ceribandin|ksw|ယုၢ်}} * {{Z|skb}}: {{W-/ceribandin|skb|หนู่}} * {{Z|se}}: {{W-/ceribandin|se|sáhpán}} * {{Z|smn}}: {{W-/ceribandin|smn|säplig}} * {{Z|sms}}: {{W-/ceribandin|sms|sä´ppli}} * {{Z|sm}}: {{W-/ceribandin|sm|'isumu}}, {{W-/ceribandin|sm|'iole}} * {{Z|sa}}: {{W+/ceribandin|sa|मूष्|n|m}}, {{W+/ceribandin|sa|आखु|n}} * {{Z|sc}}: {{W-/ceribandin|sc|tope}}, {{W-/ceribandin|sc|topi}} *: {{Z|sc}}: {{W-/ceribandin|sc|medrona|m}}, {{W-/ceribandin|sc|topi}}, {{W-/ceribandin|sc|sórixi|n}} * {{Z|ceb}}: {{W-/ceribandin|ceb|ilaga}} * {{Z|scn}}: {{W+/ceribandin|scn|surci|n}}, {{W+/ceribandin|scn|sùrici|n}} * {{Z|sd}}: {{W-/ceribandin|sd|ڪوئو|tr=kūʾō|cuda=ڪوُئو|sc=sd-Arab}} * {{Z|sh}}: *: Kirîlî: {{W-/ceribandin|sh|ми̏ш|n}} *: Latînî: {{W+/ceribandin|sh|mȉš|n}} * {{Z|sco}}: {{W-/ceribandin|sco|moose}} * {{Z|cu}}: *: Kirîlî: {{W-/ceribandin|cu|мꙑшь|m}} * {{Z|orv}}: {{W-/ceribandin|orv|мꙑшь|m}} * {{Z|sk}}: {{W+/ceribandin|sk|myš|m}} * {{Z|sl}}: {{W+/ceribandin|sl|miš|m}} * {{Z|so}}: {{W+/ceribandin|so|jiir|n}} * Sorbî: *: {{Z|hsb}}: {{W-/ceribandin|hsb|myš|m}}, {{W-/ceribandin|hsb|myška|m}} *: {{Z|dsb}}: {{W-/ceribandin|dsb|myš|m}}, {{W-/ceribandin|dsb|myška|}} * {{Z|st}}: {{W+/ceribandin|st|tweba}} * {{Z|es}}: {{W+/ceribandin|es|ratón|n}}, {{W+/ceribandin|es|mur|n}} * {{Z|sw}}: {{W+/ceribandin|sw|kipanya}} * {{Z|sv}}: {{W+/ceribandin|sv|mus|g}} * {{Z|shn}}: {{W-/ceribandin|shn|ၼူ}} * {{Z|tg}}: {{W+/ceribandin|tg|муш|sc=Cyrl}} * {{Z|tl}}: {{W+/ceribandin|tl|daga}} * {{Z|tht}}: {{W-/ceribandin|tht|dlūne}} * {{Z|tly}}: {{W-/ceribandin|tly|морә|tr=morǝ}} * {{Z|ta}}: {{W+/ceribandin|ta|சுண்டெலி|sc=Taml}} * {{Z|twf}}: {{W-/ceribandin|twf|cìyúna}} * {{Z|roa-tar}}: {{W-/ceribandin|roa-tar|topo|n}} * {{Z|rif}}: {{W-/ceribandin|rif|aɣerḍa|n}} * {{Z|blt}}: {{W-/ceribandin|blt|ꪘꪴ}} * {{Z|nod}}: {{W-/ceribandin|nod|ᩉ᩠ᨶᩪ}} * {{Z|th}}: {{W+/ceribandin|th|หนู}} * {{Z|te}}: {{W+/ceribandin|te|ఎలుక|sc=Telu}} * {{Z|crh}}: {{W-/ceribandin|crh|sıçan}} * {{Z|tt}}: {{W+/ceribandin|tt|тычкан|sc=Cyrl}} * {{Z|ti}}: {{W-/ceribandin|ti|ኣንጭዋ}} * {{Z|tr}}: {{W+/ceribandin|tr|fare}}, {{W+/ceribandin|tr|sıçan}} * {{Z|tk}}: {{W-/ceribandin|tk|syçan}}, {{W-/ceribandin|tk|sıçan}} * {{Z|bo}}: {{W-/ceribandin|bo|ཙི་ཙི|sc=Tibt}} * {{Z|tli}}: {{W-/ceribandin|tli|kagaák}} * {{Z|tpi}}: {{W-/ceribandin|tpi|liklik rat}} * {{Z|to}}: {{W-/ceribandin|to|kumā}} * {{Z|tn}}: {{W-/ceribandin|tn|pêba}} * {{Z|tum}}: {{W-/ceribandin|tum|mbewa}} * {{Z|udm}}: {{W-/ceribandin|udm|шыр}} * {{Z|ur}}: {{W-/ceribandin|ur|چوہا|n|tr=cūhā|sc=ur-Arab}} * {{Z|uk}}: {{W+/ceribandin|uk|ми́ша|m}}, {{W-/ceribandin|uk|миш|m}}, {{W-/ceribandin|uk|ми́шка|m}}, {{W-/ceribandin|uk|миша́к|n}} * {{Z|vec}}: {{W-/ceribandin|vec|ràto}}, {{W-/ceribandin|vec|sorze}} * {{Z|vi}}: {{W+/ceribandin|vi|chuột}} * {{Z|vo}}: {{W+/ceribandin|vo|mug}} * {{Z|wa}}: {{W+/ceribandin|wa|sori|m}} * {{Z|cy}}: {{W+/ceribandin|cy|llygoden|m}}, {{W+/ceribandin|cy|llygod|p}} * {{Z|wym}}: {{W-/ceribandin|wym|maojs}} * {{Z|wo}}: {{W-/ceribandin|wo|janax|cuda=janax ji}} * {{Z|km}}: {{W-/ceribandin|km|កណ្ដុរស្រូវ}}, {{W+/ceribandin|km|កណ្ដុរ}} * Kipchak: *: {{Z|qwm}}: {{W-/ceribandin|qwm|սըչխան|tr=sïč̣χan}} * {{Z|xh}}: {{W-/ceribandin|xh|ilifu}} * {{Z|rys}}: {{W-/ceribandin|rys|うぇんちゅ|tr=Wenchu}} * {{Z|yai}}: {{W-/ceribandin|yai|муш}} * {{Z|sah}}: {{W-/ceribandin|sah|кутуйах|sc=Cyrl}} * {{Z|yag}}: {{W-/ceribandin|yag|wasána}} * {{Z|yi}}: {{W-/ceribandin|yi|מויז|sc=Hebr}} * {{Z|yoi}}: {{W-/ceribandin|yoi|うやんとぅ|tr=uyantu}} * {{Z|yo}}: {{W-/ceribandin|yo|èkúté}}, {{W-/ceribandin|yo|eliri}} * {{Z|el}}: {{W+/ceribandin|el|ποντίκι|nt}}, {{W+/ceribandin|el|ποντικός|n|sc=Grek}} *: {{Z|grc}}: {{W-/ceribandin|grc|μῦς|n}}, {{W-/ceribandin|grc|ποντικός|n}} * {{Z|za}}: {{W-/ceribandin|za|nou}} * {{Z|zu}}: {{W+/ceribandin|zu|igundane|c5|c6}} {{werger-bin/ceribandin}} :{{werger çavkanî|en|mouse|oldid=75626351|beş=Ev qismê Wergerê}} i77v428b5pdrndcpj5l0c74y0rat2o1 Bikarhêner:Balyozbot/zimanên di bin zimanan de 2 1359455 6794069 2026-10-14T20:26:58Z Balyozbot 7867 Bot: rêzên wergerê yên di bin zimanan de, ji dumpê 6794069 wikitext text/x-wiki Dump: <code>kuwiktionary-20261001-pages-articles.xml.bz2</code>, 1035951 rûpel (nav-cih 0). Rêzên di beşên Werger de yên di bin rêzeke zimanekî din de. "Wekî rêza serekî": çend caran ev ziman di rêzeke serekî de hatiye nivîsîn. == Cot (863) == {| class="wikitable sortable" ! Ziman !! Di bin !! Hejmar !! Wekî rêza serekî !! Nimûne |- | <nowiki>cmn</nowiki> || <nowiki>zh</nowiki> || 2731 || 55 || [[lêker]], [[rengdêr]], [[mişk]] |- | <nowiki>kirîlî</nowiki> || <nowiki>sh</nowiki> || 2427 || 8 || [[lêker]], [[rengdêr]], [[mişk]] |- | <nowiki>latînî</nowiki> || <nowiki>sh</nowiki> || 2399 || 5 || [[lêker]], [[rengdêr]], [[mişk]] |- | <nowiki>nb</nowiki> || <nowiki>no</nowiki> || 1704 || 88 || [[lêker]], [[xulek]], [[werger]] |- | <nowiki>nn</nowiki> || <nowiki>no</nowiki> || 1411 || 121 || [[lêker]], [[rengdêr]], [[xulek]] |- | <nowiki>yue</nowiki> || <nowiki>zh</nowiki> || 1262 || 27 || [[lêker]], [[rengdêr]], [[mişk]] |- | <nowiki>kevn</nowiki> || <nowiki>el</nowiki> || 1153 || 0 || [[rengdêr]], [[mêr]], [[mêr]] |- | <nowiki>arz</nowiki> || <nowiki>ar</nowiki> || 1027 || 20 || [[lêker]], [[mişk]], [[mij]] |- | <nowiki>kirîlî</nowiki> || <nowiki>mn</nowiki> || 917 || 8 || [[rengdêr]], [[xulek]], [[werger]] |- | <nowiki>dsb</nowiki> || <nowiki>sorbî</nowiki> || 889 || 85 || [[lêker]], [[mişk]], [[xulek]] |- | <nowiki>nan</nowiki> || <nowiki>zh</nowiki> || 767 || 37 || [[lêker]], [[rengdêr]], [[mişk]] |- | <nowiki>acw</nowiki> || <nowiki>ar</nowiki> || 673 || 0 || [[mij]], [[jin]], [[jin]] |- | <nowiki>hsb</nowiki> || <nowiki>sorbî</nowiki> || 642 || 110 || [[lêker]], [[mişk]], [[mij]] |- | <nowiki>dng</nowiki> || <nowiki>zh</nowiki> || 566 || 3 || [[mişk]], [[xulek]], [[werger]] |- | <nowiki>ary</nowiki> || <nowiki>ar</nowiki> || 497 || 1 || [[mişk]], [[xulek]], [[xulek]] |- | <nowiki>kirîlî</nowiki> || <nowiki>cu</nowiki> || 475 || 8 || [[mişk]], [[mij]], [[jin]] |- | <nowiki>hak</nowiki> || <nowiki>zh</nowiki> || 462 || 1 || [[mişk]], [[xulek]], [[werger]] |- | <nowiki>wuu</nowiki> || <nowiki>zh</nowiki> || 421 || 0 || [[mişk]], [[xulek]], [[werger]] |- | <nowiki>mongolî</nowiki> || <nowiki>mn</nowiki> || 386 || 6 || [[rengdêr]], [[xulek]], [[werger]] |- | <nowiki>cdo</nowiki> || <nowiki>zh</nowiki> || 369 || 0 || [[mişk]], [[xulek]], [[werger]] |- | <nowiki>grc</nowiki> || <nowiki>el</nowiki> || 368 || 104 || [[lêker]], [[mişk]], [[mij]] |- | <nowiki>nds-de</nowiki> || <nowiki>nds</nowiki> || 289 || 6 || [[lêker]], [[rengdêr]], [[mişk]] |- | <nowiki>gsw</nowiki> || <nowiki>de</nowiki> || 260 || 93 || [[mişk]], [[mêr]], [[îro]] |- | <nowiki>ajp</nowiki> || <nowiki>ar</nowiki> || 234 || 0 || [[jin]], [[jin]], [[mêr]] |- | <nowiki>sga</nowiki> || <nowiki>ga</nowiki> || 219 || 35 || [[jin]], [[mêr]], [[îro]] |- | <nowiki>syc</nowiki> || <nowiki>arc</nowiki> || 213 || 32 || [[jin]], [[mêr]], [[dem]] |- | <nowiki>afb</nowiki> || <nowiki>ar</nowiki> || 189 || 0 || [[xulek]], [[mêr]], [[mêr]] |- | <nowiki>xcl</nowiki> || <nowiki>hy</nowiki> || 175 || 6 || [[mişk]], [[xulek]], [[pêr]] |- | <nowiki>fro</nowiki> || <nowiki>fr</nowiki> || 155 || 110 || [[rengdêr]], [[dem]], [[qencî]] |- | <nowiki>apw</nowiki> || <nowiki>apaçî</nowiki> || 153 || 23 || [[mij]], [[kompûter]], [[heyv]] |- | <nowiki>frr</nowiki> || <nowiki>frr</nowiki> || 153 || 328 || [[mêr]], [[mêr]], [[mêr]] |- | <nowiki>no</nowiki> || <nowiki>no</nowiki> || 128 || 4387 || [[rengdêr]], [[gel]], [[mirov]] |- | <nowiki>siryanî</nowiki> || <nowiki>arc</nowiki> || 127 || 0 || [[lêker]], [[mij]], [[sed]] |- | <nowiki>mhr</nowiki> || <nowiki>marî</nowiki> || 125 || 84 || [[mişk]], [[duşem]], [[heyv]] |- | <nowiki>se</nowiki> || <nowiki>samî</nowiki> || 119 || 679 || [[mij]], [[jin]], [[mêr]] |- | <nowiki>glagolîtîk</nowiki> || <nowiki>cu</nowiki> || 115 || 0 || [[jin]], [[jin]], [[mêr]] |- | <nowiki>aii</nowiki> || <nowiki>arc</nowiki> || 111 || 295 || [[mêr]], [[baran]], [[mirov]] |- | <nowiki>îbranî</nowiki> || <nowiki>arc</nowiki> || 103 || 4 || [[lêker]], [[mij]], [[sed]] |- | <nowiki>smn</nowiki> || <nowiki>samî</nowiki> || 99 || 81 || [[mij]], [[jin]], [[mêr]] |- | <nowiki>sms</nowiki> || <nowiki>samî</nowiki> || 90 || 151 || [[mij]], [[mêr]], [[duşem]] |- | <nowiki>nds-nl</nowiki> || <nowiki>nds</nowiki> || 88 || 3 || [[îro]], [[paytext]], [[heyv]] |- | <nowiki>gan</nowiki> || <nowiki>zh</nowiki> || 84 || 0 || [[mij]], [[yek]], [[du]] |- | <nowiki>cjy</nowiki> || <nowiki>zh</nowiki> || 83 || 0 || [[mij]], [[yek]], [[çav]] |- | <nowiki>roman</nowiki> || <nowiki>sh</nowiki> || 81 || 1 || [[ko]], [[dirav]], [[dev]] |- | <nowiki>apc</nowiki> || <nowiki>ar</nowiki> || 81 || 0 || [[jin]], [[çav]], [[dev]] |- | <nowiki>sma</nowiki> || <nowiki>samî</nowiki> || 80 || 62 || [[mêr]], [[duşem]], [[ferheng]] |- | <nowiki>mrj</nowiki> || <nowiki>marî</nowiki> || 76 || 1 || [[mişk]], [[malbat]], [[duşem]] |- | <nowiki>hsn</nowiki> || <nowiki>zh</nowiki> || 76 || 0 || [[mij]], [[yek]], [[heft]] |- | <nowiki>sjd</nowiki> || <nowiki>samî</nowiki> || 73 || 18 || [[mij]], [[malbat]], [[baran]] |- | <nowiki>mnp</nowiki> || <nowiki>zh</nowiki> || 72 || 0 || [[mij]], [[mêr]], [[mal]] |- | <nowiki>frm</nowiki> || <nowiki>fr</nowiki> || 71 || 10 || [[jin]], [[dem]], [[ferheng]] |- | <nowiki>gurmuxî</nowiki> || <nowiki>puncabî</nowiki> || 70 || 0 || [[jin]], [[şev]], [[berf]] |- | <nowiki>logudorî</nowiki> || <nowiki>sc</nowiki> || 60 || 0 || [[xulek]], [[pêr]], [[qerax]] |- | <nowiki>kirîlî</nowiki> || <nowiki>uz</nowiki> || 58 || 8 || [[ferheng]], [[baran]], [[dayik]] |- | <nowiki>kampîdanî</nowiki> || <nowiki>sc</nowiki> || 57 || 0 || [[pêr]], [[qerax]], [[xapandin]] |- | <nowiki>rûmî</nowiki> || <nowiki>malay</nowiki> || 56 || 0 || [[duşem]], [[dem]], [[yek]] |- | <nowiki>sc</nowiki> || <nowiki>sardinian</nowiki> || 56 || 670 || [[reş]], [[reş]], [[reş]] |- | <nowiki>mooring</nowiki> || <nowiki>frr</nowiki> || 55 || 0 || [[duşem]], [[sor]], [[yek]] |- | <nowiki>acm</nowiki> || <nowiki>ar</nowiki> || 55 || 0 || [[çav]], [[dev]], [[çawa]] |- | <nowiki>ms</nowiki> || <nowiki>malay</nowiki> || 54 || 2601 || [[duşem]], [[dem]], [[yek]] |- | <nowiki>şaxmuxî</nowiki> || <nowiki>puncabî</nowiki> || 50 || 0 || [[jin]], [[şev]], [[berf]] |- | <nowiki>aeb</nowiki> || <nowiki>ar</nowiki> || 50 || 0 || [[baş]], [[qenc]], [[rind]] |- | <nowiki>latînî</nowiki> || <nowiki>lad</nowiki> || 47 || 5 || [[feqîr]], [[yek]], [[dev]] |- | <nowiki>tly</nowiki> || <nowiki>talysh</nowiki> || 46 || 96 || [[îro]], [[sor]], [[şev]] |- | <nowiki>tmr</nowiki> || <nowiki>arc</nowiki> || 46 || 0 || [[heyv]], [[baran]], [[yek]] |- | <nowiki>gmw-cfr</nowiki> || <nowiki>de</nowiki> || 45 || 15 || [[xulek]], [[heyv]], [[dev]] |- | <nowiki>sc</nowiki> || <nowiki>sc</nowiki> || 44 || 670 || [[mişk]], [[çav]], [[çar]] |- | <nowiki>shi</nowiki> || <nowiki>berberî</nowiki> || 43 || 66 || [[jin]], [[mêr]], [[malbat]] |- | <nowiki>bohaîrî</nowiki> || <nowiki>cop</nowiki> || 42 || 0 || [[jin]], [[mêr]], [[duşem]] |- | <nowiki>klasîk</nowiki> || <nowiki>nahuatl</nowiki> || 42 || 0 || [[mêr]], [[baran]], [[mirov]] |- | <nowiki>sahîdî</nowiki> || <nowiki>cop</nowiki> || 41 || 0 || [[jin]], [[mêr]], [[şev]] |- | <nowiki>adlamî</nowiki> || <nowiki>ff</nowiki> || 40 || 0 || [[kompûter]], [[duşem]], [[ferheng]] |- | <nowiki>latînî</nowiki> || <nowiki>ff</nowiki> || 40 || 5 || [[kompûter]], [[duşem]], [[ferheng]] |- | <nowiki>şahmuxî</nowiki> || <nowiki>punjabi</nowiki> || 40 || 0 || [[malbat]], [[mal]], [[pazde]] |- | <nowiki>os</nowiki> || <nowiki>ossetian</nowiki> || 36 || 646 || [[duşem]], [[duşem]], [[ferheng]] |- | <nowiki>gurmuxî</nowiki> || <nowiki>punjabi</nowiki> || 34 || 0 || [[malbat]], [[mal]], [[pazde]] |- | <nowiki>aramiya cihû</nowiki> || <nowiki>arc</nowiki> || 34 || 0 || [[didan]], [[dest]], [[ez]] |- | <nowiki>rmf</nowiki> || <nowiki>rom</nowiki> || 33 || 0 || [[sed]], [[bîst]], [[yek]] |- | <nowiki>ar</nowiki> || <nowiki>ar</nowiki> || 33 || 11779 || [[mal]], [[deh]], [[Nîjer]] |- | <nowiki>kirîlî</nowiki> || <nowiki>az</nowiki> || 30 || 8 || [[ferheng]], [[baran]], [[kurdî]] |- | <nowiki>ms</nowiki> || <nowiki>ms</nowiki> || 30 || 2601 || [[spî]], [[reş]], [[deh]] |- | <nowiki>gmh</nowiki> || <nowiki>de</nowiki> || 29 || 17 || [[lêker]], [[lêker]], [[şev]] |- | <nowiki>smj</nowiki> || <nowiki>samî</nowiki> || 29 || 26 || [[ferheng]], [[şev]], [[çav]] |- | <nowiki>az</nowiki> || <nowiki>az</nowiki> || 28 || 2397 || [[paytext]], [[paytext]], [[ferheng]] |- | <nowiki>fa</nowiki> || <nowiki>fa</nowiki> || 28 || 16419 || [[dapîr]], [[dapîr]], [[nêrîn]] |- | <nowiki>arq</nowiki> || <nowiki>ar</nowiki> || 28 || 0 || [[peyivîn]], [[kirin]], [[kitêb]] |- | <nowiki>pdc</nowiki> || <nowiki>de</nowiki> || 27 || 201 || [[feqîr]], [[sî]], [[kitêb]] |- | <nowiki>mandarînî</nowiki> || <nowiki>zh</nowiki> || 27 || 7 || [[pel]], [[xwarza]], [[polîtîka]] |- | <nowiki>goh</nowiki> || <nowiki>de</nowiki> || 25 || 68 || [[lêker]], [[spî]], [[çep]] |- | <nowiki>nhn</nowiki> || <nowiki>nahuatl</nowiki> || 25 || 6 || [[mêr]], [[baran]], [[mirov]] |- | <nowiki>bar</nowiki> || <nowiki>de</nowiki> || 24 || 154 || [[îro]], [[dev]], [[dayik]] |- | <nowiki>bcl</nowiki> || <nowiki>bikol central</nowiki> || 24 || 436 || [[çima]], [[çima]], [[çima]] |- | <nowiki>îbranî</nowiki> || <nowiki>lad</nowiki> || 23 || 4 || [[yek]], [[kitêb]], [[xuh]] |- | <nowiki></nowiki> || <nowiki>zh</nowiki> || 22 || 0 || [[jin]], [[jin]], [[du]] |- | <nowiki>iu</nowiki> || <nowiki>inuktitut</nowiki> || 22 || 239 || [[feqîr]], [[nerm]], [[nerm]] |- | <nowiki>fy</nowiki> || <nowiki>frisian</nowiki> || 22 || 2298 || [[ferheng]], [[til]], [[kûçik]] |- | <nowiki>gmy</nowiki> || <nowiki>el</nowiki> || 22 || 3 || [[spî]], [[yek]], [[zevî]] |- | <nowiki>kirîlî</nowiki> || <nowiki>tk</nowiki> || 21 || 8 || [[dayik]], [[Efxanistan]], [[Pakistan]] |- | <nowiki>bokmål</nowiki> || <nowiki>no</nowiki> || 19 || 0 || [[wergêr]], [[silav]], [[gewrî]] |- | <nowiki>osp</nowiki> || <nowiki>es</nowiki> || 19 || 1 || [[jin]], [[peyivîn]], [[dest]] |- | <nowiki>nan-tws</nowiki> || <nowiki>zh</nowiki> || 19 || 0 || [[mêr]], [[yek]], [[rind]] |- | <nowiki>cjm</nowiki> || <nowiki>cham</nowiki> || 19 || 33 || [[ferheng]], [[baran]], [[du]] |- | <nowiki>latînî</nowiki> || <nowiki>az</nowiki> || 19 || 5 || [[kurdî]], [[Japonya]], [[Filipîn]] |- | <nowiki>şahmuxî</nowiki> || <nowiki>puncabî</nowiki> || 19 || 0 || [[kur]], [[Efxanistan]], [[Hindistan]] |- | <nowiki>sq</nowiki> || <nowiki>sq</nowiki> || 19 || 2799 || [[Efxanistan]], [[ala]], [[Îtalya]] |- | <nowiki>arc</nowiki> || <nowiki>arc</nowiki> || 18 || 480 || [[mêr]], [[gel]], [[ferheng]] |- | <nowiki>frr</nowiki> || <nowiki>frisian</nowiki> || 18 || 328 || [[ferheng]], [[til]], [[kûçik]] |- | <nowiki>pi</nowiki> || <nowiki>pali</nowiki> || 18 || 198 || [[mirov]], [[mirov]], [[pel]] |- | <nowiki>karakanî</nowiki> || <nowiki>jv</nowiki> || 17 || 0 || [[ferheng]], [[nûçe]], [[pare]] |- | <nowiki>rmy</nowiki> || <nowiki>rom</nowiki> || 17 || 5 || [[reş]], [[keç]], [[keçik]] |- | <nowiki>qu</nowiki> || <nowiki>qu</nowiki> || 17 || 868 || [[didan]], [[zik]], [[zik]] |- | <nowiki>pal</nowiki> || <nowiki>fa</nowiki> || 17 || 83 || [[berf]], [[file]], [[kûçik]] |- | <nowiki>avd</nowiki> || <nowiki>alviri-vidari</nowiki> || 16 || 47 || [[îro]], [[kesk]], [[çav]] |- | <nowiki>unm</nowiki> || <nowiki>lenape</nowiki> || 16 || 69 || [[pênc]], [[çar]], [[deh]] |- | <nowiki>mga</nowiki> || <nowiki>ga</nowiki> || 16 || 1 || [[sî]], [[rî]], [[şivan]] |- | <nowiki>as</nowiki> || <nowiki>assamese</nowiki> || 16 || 936 || [[karîn]], [[karîn]], [[şiyan]] |- | <nowiki>lld</nowiki> || <nowiki>ladin</nowiki> || 15 || 299 || [[îro]], [[îro]], [[îro]] |- | <nowiki>pi</nowiki> || <nowiki>pi</nowiki> || 15 || 198 || [[derî]], [[derî]], [[derî]] |- | <nowiki>lzh</nowiki> || <nowiki>zh</nowiki> || 14 || 1 || [[mişk]], [[mêr]], [[paytext]] |- | <nowiki>gmw-ecg</nowiki> || <nowiki>de</nowiki> || 14 || 31 || [[dem]], [[dev]], [[dapîr]] |- | <nowiki>föhr</nowiki> || <nowiki>frr</nowiki> || 14 || 0 || [[spî]], [[reş]], [[dapîr]] |- | <nowiki>sje</nowiki> || <nowiki>samî</nowiki> || 14 || 13 || [[şev]], [[bav]], [[bab]] |- | <nowiki>sh</nowiki> || <nowiki>sh</nowiki> || 14 || 4502 || [[kirin]], [[deqe]], [[deqe]] |- | <nowiki>rmf</nowiki> || <nowiki>romani</nowiki> || 13 || 0 || [[rengdêr]], [[paytext]], [[ferheng]] |- | <nowiki>nynorsk</nowiki> || <nowiki>no</nowiki> || 13 || 0 || [[wergêr]], [[banknot]], [[bestenî]] |- | <nowiki>gmw-rfr</nowiki> || <nowiki>de</nowiki> || 13 || 10 || [[jin]], [[dev]], [[nû]] |- | <nowiki>nch</nowiki> || <nowiki>nahuatl</nowiki> || 13 || 2 || [[mêr]], [[kûçik]], [[teyr]] |- | <nowiki>kirîlî</nowiki> || <nowiki>ro</nowiki> || 13 || 8 || [[baran]], [[yek]], [[dev]] |- | <nowiki>egy</nowiki> || <nowiki>ar</nowiki> || 13 || 372 || [[sê]], [[ew]], [[hemû]] |- | <nowiki>latînî</nowiki> || <nowiki>jv</nowiki> || 13 || 5 || [[peyv]], [[gelek]], [[gelek]] |- | <nowiki>sjt</nowiki> || <nowiki>samî</nowiki> || 12 || 7 || [[mij]], [[şev]], [[rî]] |- | <nowiki>mn</nowiki> || <nowiki>mn</nowiki> || 12 || 1927 || [[sed]], [[yek]], [[ser]] |- | <nowiki>kirîlî</nowiki> || <nowiki>ug</nowiki> || 12 || 8 || [[ferheng]], [[Efxanistan]], [[Pakistan]] |- | <nowiki>oge</nowiki> || <nowiki>ka</nowiki> || 12 || 1 || [[dest]], [[Spanya]], [[paşa]] |- | <nowiki>apd</nowiki> || <nowiki>ar</nowiki> || 11 || 0 || [[mêr]], [[gelek]], [[pir]] |- | <nowiki>stq</nowiki> || <nowiki>frisian</nowiki> || 11 || 189 || [[ferheng]], [[kûçik]], [[hirç]] |- | <nowiki>amw</nowiki> || <nowiki>arc</nowiki> || 11 || 0 || [[yek]], [[pênc]], [[du]] |- | <nowiki>hallig</nowiki> || <nowiki>frr</nowiki> || 11 || 0 || [[reş]], [[dapîr]], [[vexwarin]] |- | <nowiki>sju</nowiki> || <nowiki>samî</nowiki> || 11 || 3 || [[şev]], [[bav]], [[bab]] |- | <nowiki>crk</nowiki> || <nowiki>cr</nowiki> || 11 || 16 || [[dev]], [[pênc]], [[pel]] |- | <nowiki>ota</nowiki> || <nowiki>tr</nowiki> || 11 || 632 || [[axa]], [[Enqere]], [[tacikî]] |- | <nowiki>kk</nowiki> || <nowiki>kk</nowiki> || 11 || 2063 || [[kitêb]], [[xwendevan]], [[Efxanistan]] |- | <nowiki>ayn</nowiki> || <nowiki>ar</nowiki> || 11 || 0 || [[taşt]], [[birçî]], [[xwastin]] |- | <nowiki>dorîk</nowiki> || <nowiki>el</nowiki> || 10 || 0 || [[biçûk]], [[vexwarin]], [[hingê]] |- | <nowiki>ks</nowiki> || <nowiki>kashmiri</nowiki> || 10 || 197 || [[ez]], [[ez]], [[Hindistan]] |- | <nowiki>gml</nowiki> || <nowiki>nds</nowiki> || 10 || 15 || [[tu]], [[ev]], [[ew]] |- | <nowiki>apj</nowiki> || <nowiki>apaçî</nowiki> || 10 || 10 || [[pare]], [[doşek]], [[silav]] |- | <nowiki>erebî</nowiki> || <nowiki>az</nowiki> || 10 || 4 || [[Japonya]], [[ensîklopedî]], [[Êrîvan]] |- | <nowiki>lmo</nowiki> || <nowiki>lombard</nowiki> || 10 || 413 || [[saet]], [[saet]], [[dû]] |- | <nowiki>kirilî</nowiki> || <nowiki>sh</nowiki> || 10 || 0 || [[navdêr]], [[gurcî]], [[amharî]] |- | <nowiki>fa-ira</nowiki> || <nowiki>persian</nowiki> || 10 || 0 || [[helbest]], [[şiîr]], [[tacikî]] |- | <nowiki>kevn</nowiki> || <nowiki>yûnanî</nowiki> || 9 || 0 || [[roj]], [[roj]], [[ser]] |- | <nowiki>ncj</nowiki> || <nowiki>nahuatl</nowiki> || 9 || 0 || [[şev]], [[nivîsîn]], [[ba]] |- | <nowiki>nhg</nowiki> || <nowiki>nahuatl</nowiki> || 9 || 1 || [[şev]], [[nivîsîn]], [[ba]] |- | <nowiki>kw</nowiki> || <nowiki>cornish</nowiki> || 9 || 781 || [[û]], [[noel]], [[noel]] |- | <nowiki>nan-hbl</nowiki> || <nowiki>zh</nowiki> || 9 || 0 || [[cins]], [[cins]], [[dîreksiyon]] |- | <nowiki>rmy</nowiki> || <nowiki>romani</nowiki> || 8 || 5 || [[ferheng]], [[çep]], [[kuş]] |- | <nowiki>azz</nowiki> || <nowiki>nahuatl</nowiki> || 8 || 0 || [[şev]], [[nivîsîn]], [[ba]] |- | <nowiki>nhx</nowiki> || <nowiki>nahuatl</nowiki> || 8 || 2 || [[şev]], [[nivîsîn]], [[ba]] |- | <nowiki>ca</nowiki> || <nowiki>ca</nowiki> || 8 || 4846 || [[heşt]], [[hejde]], [[heştê]] |- | <nowiki>wlm</nowiki> || <nowiki>cy</nowiki> || 8 || 2 || [[ez]], [[kûçik]], [[paşa]] |- | <nowiki>krîlî</nowiki> || <nowiki>sh</nowiki> || 8 || 0 || [[ap]], [[mam]], [[siberoj]] |- | <nowiki>axm</nowiki> || <nowiki>hy</nowiki> || 8 || 0 || [[cihû]], [[hêk]], [[bacanreş]] |- | <nowiki>tzm</nowiki> || <nowiki>berberî</nowiki> || 7 || 140 || [[jin]], [[xanî]], [[goşt]] |- | <nowiki>acy</nowiki> || <nowiki>ar</nowiki> || 7 || 0 || [[duh]], [[dêr]], [[Beyrût]] |- | <nowiki>pa</nowiki> || <nowiki>punjabi</nowiki> || 7 || 751 || [[paytext]], [[paytext]], [[masîgir]] |- | <nowiki>kab</nowiki> || <nowiki>berberî</nowiki> || 7 || 113 || [[mirov]], [[goşt]], [[gerdûn]] |- | <nowiki>kirîlî</nowiki> || <nowiki>sirboxirwatî</nowiki> || 7 || 8 || [[roj]], [[roj]], [[baneşan]] |- | <nowiki>şahmuxî</nowiki> || <nowiki>pa</nowiki> || 7 || 0 || [[dev]], [[û]], [[keştî]] |- | <nowiki>kca</nowiki> || <nowiki>khanty</nowiki> || 7 || 33 || [[guh]], [[gelek]], [[pir]] |- | <nowiki>cmg</nowiki> || <nowiki>mn</nowiki> || 7 || 1 || [[ez]], [[ev]], [[cih]] |- | <nowiki>latînî</nowiki> || <nowiki>be</nowiki> || 7 || 5 || [[Rûsya]], [[Ukrayna]], [[Polonya]] |- | <nowiki>ars</nowiki> || <nowiki>ar</nowiki> || 7 || 0 || [[Îngilistan]], [[gû]], [[qûn]] |- | <nowiki>pro</nowiki> || <nowiki>oc</nowiki> || 7 || 85 || [[dijmin]], [[stranbêj]], [[pîroz]] |- | <nowiki>lad</nowiki> || <nowiki>lad</nowiki> || 7 || 417 || [[zimannasî]], [[hinar]], [[hinar]] |- | <nowiki>nds</nowiki> || <nowiki>nds</nowiki> || 7 || 736 || [[în]], [[pêncşem]], [[paşa]] |- | <nowiki>nb</nowiki> || <nowiki>norwecî</nowiki> || 7 || 88 || [[navdêr]], [[çekî]], [[çîniya sade]] |- | <nowiki>el</nowiki> || <nowiki>el</nowiki> || 6 || 4553 || [[Belçîka]], [[vir]], [[pêşnav]] |- | <nowiki>rw</nowiki> || <nowiki>rwanda-rundi</nowiki> || 6 || 185 || [[Swêd]], [[silav]], [[giya]] |- | <nowiki>pox</nowiki> || <nowiki>polabian</nowiki> || 6 || 124 || [[kûçik]], [[kûçik]], [[kûçik]] |- | <nowiki>tru</nowiki> || <nowiki>arc</nowiki> || 6 || 7 || [[rojava]], [[gor]], [[gulan]] |- | <nowiki>{{zh-tc}}</nowiki> || <nowiki>zh</nowiki> || 6 || 1 || [[Wîkîferheng]], [[spanî]], [[danmarkî]] |- | <nowiki>zh</nowiki> || <nowiki>zh</nowiki> || 6 || 4495 || [[kolan]], [[biyanî]], [[kelek]] |- | <nowiki>nn</nowiki> || <nowiki>norwecî</nowiki> || 6 || 121 || [[navdêr]], [[çekî]], [[çîniya sade]] |- | <nowiki>{{cmn}}</nowiki> || <nowiki>zh</nowiki> || 6 || 0 || [[mamoste]], [[teşqilîn]], [[şênber]] |- | <nowiki>prs</nowiki> || <nowiki>persian</nowiki> || 6 || 0 || [[helbest]], [[şiîr]], [[tacik]] |- | <nowiki>flemish</nowiki> || <nowiki>nl</nowiki> || 6 || 6 || [[rojbûna te pîroz be]], [[bi xêra xwe]], [[ji qenciya xwe]] |- | <nowiki>pra-sau</nowiki> || <nowiki>prakrîtî</nowiki> || 5 || 0 || [[jin]], [[yek]], [[tîp]] |- | <nowiki>cop</nowiki> || <nowiki>cop</nowiki> || 5 || 322 || [[mêr]], [[jîn]], [[term]] |- | <nowiki>cja</nowiki> || <nowiki>cham</nowiki> || 5 || 9 || [[ferheng]], [[gund]], [[kûçik]] |- | <nowiki>latînî</nowiki> || <nowiki>pali</nowiki> || 5 || 5 || [[mirov]], [[pel]], [[gund]] |- | <nowiki>rml</nowiki> || <nowiki>rom</nowiki> || 5 || 0 || [[reş]], [[bav]], [[bab]] |- | <nowiki>ngu</nowiki> || <nowiki>nahuatl</nowiki> || 5 || 0 || [[şev]], [[ba]], [[çiya]] |- | <nowiki>sia</nowiki> || <nowiki>samî</nowiki> || 5 || 3 || [[çav]], [[rî]], [[bav]] |- | <nowiki>iu</nowiki> || <nowiki>iu</nowiki> || 5 || 239 || [[guh]], [[guh]], [[guh]] |- | <nowiki>peo</nowiki> || <nowiki>fa</nowiki> || 5 || 32 || [[ez]], [[Firat]], [[Dîcle]] |- | <nowiki>mandarin</nowiki> || <nowiki>zh</nowiki> || 5 || 0 || [[ku]], [[bê]], [[-kujî]] |- | <nowiki></nowiki> || <nowiki>nds</nowiki> || 5 || 0 || [[bûn]], [[yekşem]], [[yekşem]] |- | <nowiki>latînî</nowiki> || <nowiki>crimean tatar</nowiki> || 5 || 5 || [[ba]], [[term]], [[gerdûn]] |- | <nowiki>fa-ira</nowiki> || <nowiki>fa</nowiki> || 5 || 0 || [[sava]], [[Enqere]], [[otobûs]] |- | <nowiki>pt</nowiki> || <nowiki>portuguese</nowiki> || 5 || 6661 || [[Kenya]], [[Kenya]], [[benîşt]] |- | <nowiki>sdc</nowiki> || <nowiki>sc</nowiki> || 5 || 69 || [[ramûsan]], [[ramûsîn]], [[ramûsîn]] |- | <nowiki>nhw</nowiki> || <nowiki>nahuatl</nowiki> || 5 || 0 || [[teyr]], [[mar]], [[dar]] |- | <nowiki>zpq</nowiki> || <nowiki>zapotec</nowiki> || 5 || 6 || [[saet]], [[dû]], [[term]] |- | <nowiki>lld</nowiki> || <nowiki>lld</nowiki> || 5 || 299 || [[dar]], [[dar]], [[dar]] |- | <nowiki>ff</nowiki> || <nowiki>ff</nowiki> || 5 || 75 || [[term]], [[hewî]], [[hewî]] |- | <nowiki>latinî</nowiki> || <nowiki>sh</nowiki> || 5 || 0 || [[gurcî]], [[amharî]], [[aragonî]] |- | <nowiki>ermeniya kevn</nowiki> || <nowiki>hy</nowiki> || 5 || 1 || [[dûmeqesk]], [[hecîreşk]], [[sipil]] |- | <nowiki>mandarin</nowiki> || <nowiki>çînî</nowiki> || 5 || 0 || [[fîlmê tirsê]], [[Budapeşt]], [[bazirganiya azad]] |- | <nowiki>jv</nowiki> || <nowiki>jv</nowiki> || 4 || 714 || [[îro]], [[îro]], [[sibe]] |- | <nowiki>nci</nowiki> || <nowiki>nahuatl</nowiki> || 4 || 157 || [[mirov]], [[Almanya]], [[mirin]] |- | <nowiki>pra-mah</nowiki> || <nowiki>prakrîtî</nowiki> || 4 || 0 || [[yek]], [[peyivîn]], [[piling]] |- | <nowiki>rmn</nowiki> || <nowiki>rom</nowiki> || 4 || 0 || [[reş]], [[bav]], [[bab]] |- | <nowiki>rmc</nowiki> || <nowiki>rom</nowiki> || 4 || 0 || [[reş]], [[bav]], [[bab]] |- | <nowiki>rmo</nowiki> || <nowiki>rom</nowiki> || 4 || 0 || [[reş]], [[bav]], [[bab]] |- | <nowiki>latînî</nowiki> || <nowiki>sirboxirwatî</nowiki> || 4 || 5 || [[roj]], [[roj]], [[se/Werger]] |- | <nowiki>sa</nowiki> || <nowiki>sa</nowiki> || 4 || 1122 || [[peyivîn]], [[kirin]], [[gotin]] |- | <nowiki>fia</nowiki> || <nowiki>nubian</nowiki> || 4 || 6 || [[bajar]], [[Misr]], [[paşa]] |- | <nowiki>pgl</nowiki> || <nowiki>ga</nowiki> || 4 || 0 || [[keç]], [[kur]], [[nav]] |- | <nowiki>rmw</nowiki> || <nowiki>rom</nowiki> || 4 || 0 || [[bav]], [[dayik]], [[bab]] |- | <nowiki>apm</nowiki> || <nowiki>apaçî</nowiki> || 4 || 9 || [[dayik]], [[kes]], [[hesp]] |- | <nowiki>ncl</nowiki> || <nowiki>nahuatl</nowiki> || 4 || 0 || [[ba]], [[gul]], [[kulîlk]] |- | <nowiki>prs</nowiki> || <nowiki>fa</nowiki> || 4 || 0 || [[sava]], [[Enqere]], [[otobûs]] |- | <nowiki>pnt</nowiki> || <nowiki>el</nowiki> || 4 || 46 || [[Gurcistan]], [[hirç]], [[garis]] |- | <nowiki>ofs</nowiki> || <nowiki>frisian</nowiki> || 4 || 73 || [[til]], [[kûçik]], [[kilîl]] |- | <nowiki>swg</nowiki> || <nowiki>de</nowiki> || 4 || 28 || [[sal]], [[çiya]], [[hêk]] |- | <nowiki>zlw-opl</nowiki> || <nowiki>pl</nowiki> || 4 || 11 || [[girav]], [[îsal]], [[derewker]] |- | <nowiki>zaa</nowiki> || <nowiki>zapotec</nowiki> || 4 || 0 || [[saet]], [[dû]], [[dûkêl]] |- | <nowiki>rûmî</nowiki> || <nowiki>ms</nowiki> || 4 || 0 || [[saet]], [[mêwîj]], [[çarêk]] |- | <nowiki>jawî</nowiki> || <nowiki>ms</nowiki> || 4 || 0 || [[saet]], [[ax]], [[ay]] |- | <nowiki>abv</nowiki> || <nowiki>ar</nowiki> || 4 || 0 || [[qûn]], [[kevçî]], [[binçeng]] |- | <nowiki>uz</nowiki> || <nowiki>uz</nowiki> || 4 || 1860 || [[riwek]], [[nebat]], [[Şerê Cîhanê yê Yekem]] |- | <nowiki>bsh</nowiki> || <nowiki>kamkata-viri</nowiki> || 4 || 26 || [[heke]], [[heke]], [[eger]] |- | <nowiki>qu</nowiki> || <nowiki>quechua</nowiki> || 4 || 868 || [[dû]], [[dû]], [[dûkêl]] |- | <nowiki>kirîlî</nowiki> || <nowiki>crimean tatar</nowiki> || 4 || 8 || [[term]], [[gerdûn]], [[bijang]] |- | <nowiki>sade</nowiki> || <nowiki>zh</nowiki> || 4 || 0 || [[melûl]], [[Neteweyên Yekbûyî]], [[bikeser]] |- | <nowiki>tradisyonal</nowiki> || <nowiki>zh</nowiki> || 4 || 0 || [[melûl]], [[Neteweyên Yekbûyî]], [[bikeser]] |- | <nowiki>de</nowiki> || <nowiki>de</nowiki> || 4 || 21709 || [[beraz]], [[beraz]], [[beraz]] |- | <nowiki>kirîlî</nowiki> || <nowiki>{{sh}}</nowiki> || 4 || 8 || [[YKSS]], [[sala nû pîroz be]], [[sersal pîroz be]] |- | <nowiki>roman</nowiki> || <nowiki>{{sh}}</nowiki> || 4 || 1 || [[YKSS]], [[sala nû pîroz be]], [[sersal pîroz be]] |- | <nowiki>ermenîermeniya kevn</nowiki> || <nowiki>hy</nowiki> || 4 || 0 || [[bergeş]], [[tepsî]], [[ferxesênî]] |- | <nowiki>rmn</nowiki> || <nowiki>romani</nowiki> || 3 || 0 || [[ferheng]], [[henek]], [[henek kirin]] |- | <nowiki>rmw</nowiki> || <nowiki>romani</nowiki> || 3 || 0 || [[ferheng]], [[hişyar]], [[Meryem]] |- | <nowiki>str</nowiki> || <nowiki>str</nowiki> || 3 || 42 || [[baran]], [[berf]], [[çiya]] |- | <nowiki>kirîlî</nowiki> || <nowiki>uzbek</nowiki> || 3 || 8 || [[kurdî]], [[nîvişk]], [[hematolojî]] |- | <nowiki>latînî</nowiki> || <nowiki>uzbek</nowiki> || 3 || 5 || [[kurdî]], [[nîvişk]], [[hematolojî]] |- | <nowiki>cwd</nowiki> || <nowiki>cr</nowiki> || 3 || 0 || [[zivistan]], [[dev]], [[guh]] |- | <nowiki>kantonî</nowiki> || <nowiki>çînî</nowiki> || 3 || 6 || [[roj]], [[roj]], [[av/Werger]] |- | <nowiki>dunganî</nowiki> || <nowiki>çînî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger]] |- | <nowiki>hakkayî</nowiki> || <nowiki>çînî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger]] |- | <nowiki>mandarînî</nowiki> || <nowiki>çînî</nowiki> || 3 || 7 || [[roj]], [[roj]], [[av/Werger]] |- | <nowiki>minnanî</nowiki> || <nowiki>çînî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger]] |- | <nowiki>wuyî</nowiki> || <nowiki>çînî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger]] |- | <nowiki>erebiya misrî</nowiki> || <nowiki>erebî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger1]] |- | <nowiki>erebiya kendavî</nowiki> || <nowiki>erebî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger1]] |- | <nowiki>erebiya hîcazî</nowiki> || <nowiki>erebî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger1]] |- | <nowiki>erebiya fasî</nowiki> || <nowiki>erebî</nowiki> || 3 || 0 || [[roj]], [[roj]], [[av/Werger1]] |- | <nowiki>kirîlî</nowiki> || <nowiki>slaviya kevn</nowiki> || 3 || 8 || [[roj]], [[se/Werger]], [[av/Werger3]] |- | <nowiki>sorbiya jêrîn</nowiki> || <nowiki>sorbî</nowiki> || 3 || 8 || [[roj]], [[roj]], [[av/Werger3]] |- | <nowiki>sorbiya jorîn</nowiki> || <nowiki>sorbî</nowiki> || 3 || 8 || [[roj]], [[roj]], [[av/Werger3]] |- | <nowiki>nuz</nowiki> || <nowiki>nahuatl</nowiki> || 3 || 0 || [[şev]], [[ba]], [[çiya]] |- | <nowiki>he</nowiki> || <nowiki>arc</nowiki> || 3 || 3830 || [[panzdeh]], [[aş]], [[necar]] |- | <nowiki>onw</nowiki> || <nowiki>nubian</nowiki> || 3 || 2 || [[bajar]], [[paşa]], [[qral]] |- | <nowiki>neo-aramiya rojavayî</nowiki> || <nowiki>arc</nowiki> || 3 || 0 || [[dotin]], [[ketin]], [[gumlek]] |- | <nowiki>rûmî</nowiki> || <nowiki>jv</nowiki> || 3 || 0 || [[nûçe]], [[pare]], [[derya]] |- | <nowiki>fr</nowiki> || <nowiki>fr</nowiki> || 3 || 9167 || [[ew]], [[saet]], [[Meryem]] |- | <nowiki>ltc</nowiki> || <nowiki>zh</nowiki> || 3 || 0 || [[Japonya]], [[Îsa]], [[Meryem]] |- | <nowiki>kirîlî</nowiki> || <nowiki>crh</nowiki> || 3 || 8 || [[Mongolya]], [[komar]], [[hûçik]] |- | <nowiki>umu</nowiki> || <nowiki>lenape</nowiki> || 3 || 8 || [[kûçik]], [[hirç]], [[se/Werger]] |- | <nowiki>{{zh-sc}}</nowiki> || <nowiki>zh</nowiki> || 3 || 3 || [[Wîkîferheng]], [[spanî]], [[danmarkî]] |- | <nowiki>naz</nowiki> || <nowiki>nahuatl</nowiki> || 3 || 0 || [[gul]], [[kulîlk]], [[pelatînk]] |- | <nowiki>zav</nowiki> || <nowiki>zapotec</nowiki> || 3 || 1 || [[saet]], [[term]], [[qehwe]] |- | <nowiki>grk-ita</nowiki> || <nowiki>el</nowiki> || 3 || 9 || [[saet]], [[dû]], [[dûkêl]] |- | <nowiki>hy</nowiki> || <nowiki>hy</nowiki> || 3 || 3043 || [[jîn]], [[hezîran]], [[Facebook]] |- | <nowiki>pra-sau</nowiki> || <nowiki>prakrît</nowiki> || 3 || 0 || [[êvar]], [[çiya]], [[herf]] |- | <nowiki>lwl</nowiki> || <nowiki>lawa</nowiki> || 3 || 1 || [[agir]], [[mûm]], [[xwê]] |- | <nowiki>mhr</nowiki> || <nowiki>mari</nowiki> || 3 || 84 || [[xewn]], [[derya]], [[zêr]] |- | <nowiki>ancient</nowiki> || <nowiki>el</nowiki> || 3 || 0 || [[qelew]], [[xwar]], [[xwehr]] |- | <nowiki>cle</nowiki> || <nowiki>chinantec</nowiki> || 3 || 0 || [[dû]], [[dûkêl]], [[qehwe]] |- | <nowiki>chz</nowiki> || <nowiki>chinantec</nowiki> || 3 || 1 || [[dû]], [[dûkêl]], [[qehwe]] |- | <nowiki>nhx</nowiki> || <nowiki>nah</nowiki> || 3 || 2 || [[dû]], [[dûkêl]], [[qehwe]] |- | <nowiki>mhr</nowiki> || <nowiki>mr</nowiki> || 3 || 84 || [[xwê]], [[zend]], [[bazin]] |- | <nowiki>nrf</nowiki> || <nowiki>norman</nowiki> || 3 || 1099 || [[gerdûn]], [[gerdûn]], [[gerdûn]] |- | <nowiki>latînî</nowiki> || <nowiki>kk</nowiki> || 3 || 5 || [[tov]], [[Qazaxistan]], [[şerê psîkolojîk]] |- | <nowiki>cpg</nowiki> || <nowiki>el</nowiki> || 3 || 4 || [[nîvişk]], [[ewr]], [[fatreşk]] |- | <nowiki>kea</nowiki> || <nowiki>kea</nowiki> || 3 || 222 || [[hezîran]], [[hezîran]], [[hezîran]] |- | <nowiki>romaji</nowiki> || <nowiki>ja</nowiki> || 3 || 0 || [[femînîzm]], [[bikeser]], [[daxbar]] |- | <nowiki>îriya kevn</nowiki> || <nowiki>ga</nowiki> || 3 || 0 || [[kinif]], [[kindir]], [[şedanok]] |- | <nowiki>roman</nowiki> || <nowiki>sirboxirwatî</nowiki> || 3 || 1 || [[baneşan]], [[Nîkaragua]], [[Kosta Rîka]] |- | <nowiki>sam</nowiki> || <nowiki>arc</nowiki> || 2 || 0 || [[mêr]], [[Mûsa]] |- | <nowiki>dorîkî</nowiki> || <nowiki>el</nowiki> || 2 || 0 || [[îro]], [[çadir]] |- | <nowiki>ug</nowiki> || <nowiki>uyghur</nowiki> || 2 || 1472 || [[bîst]], [[bîst]] |- | <nowiki>rmc</nowiki> || <nowiki>romani</nowiki> || 2 || 0 || [[ferheng]], [[kuş]] |- | <nowiki>rmo</nowiki> || <nowiki>romani</nowiki> || 2 || 0 || [[ferheng]], [[Spanya]] |- | <nowiki>cjm</nowiki> || <nowiki>çamî</nowiki> || 2 || 33 || [[sor]], [[dev]] |- | <nowiki>hur</nowiki> || <nowiki>hur</nowiki> || 2 || 19 || [[sor]], [[reş]] |- | <nowiki>apaçiya rojava</nowiki> || <nowiki>apaçî</nowiki> || 2 || 0 || [[roj]], [[av/Werger]] |- | <nowiki>aramiya nû ya asûrî</nowiki> || <nowiki>aramî</nowiki> || 2 || 1 || [[roj]], [[roj]] |- | <nowiki>siryaniya klasîk</nowiki> || <nowiki>aramî</nowiki> || 2 || 1 || [[roj]], [[roj]] |- | <nowiki>nahuatliya navendî</nowiki> || <nowiki>nahuatl</nowiki> || 2 || 0 || [[roj]], [[av/Werger2]] |- | <nowiki>almaniya navendî ya rojhilat</nowiki> || <nowiki>almanî</nowiki> || 2 || 0 || [[roj]], [[av/Werger]] |- | <nowiki>min dongî</nowiki> || <nowiki>çînî</nowiki> || 2 || 0 || [[roj]], [[av/Werger]] |- | <nowiki>fransiya kevn</nowiki> || <nowiki>fransî</nowiki> || 2 || 1 || [[roj]], [[av/Werger1]] |- | <nowiki>fia</nowiki> || <nowiki>nobiin</nowiki> || 2 || 6 || [[çav]], [[çav]] |- | <nowiki>gurmuxî</nowiki> || <nowiki>pa</nowiki> || 2 || 0 || [[dev]], [[bi xêr bên]] |- | <nowiki>och</nowiki> || <nowiki>zh</nowiki> || 2 || 0 || [[çar]], [[tu]] |- | <nowiki>he</nowiki> || <nowiki>he</nowiki> || 2 || 3830 || [[yazde]], [[guh]] |- | <nowiki>teochew</nowiki> || <nowiki>zh</nowiki> || 2 || 0 || [[sêzde]], [[zilam]] |- | <nowiki>{{syr}}</nowiki> || <nowiki>arc</nowiki> || 2 || 0 || [[panzdeh]], [[cam]] |- | <nowiki>hy</nowiki> || <nowiki>ar</nowiki> || 2 || 3043 || [[şêst]], [[heftê]] |- | <nowiki>latînî</nowiki> || <nowiki>cu</nowiki> || 2 || 5 || [[stû]], [[qanûn]] |- | <nowiki>cst</nowiki> || <nowiki>ohlone</nowiki> || 2 || 71 || [[xwelî]], [[derya]] |- | <nowiki>german low german</nowiki> || <nowiki>nds</nowiki> || 2 || 0 || [[dîtin]], [[melek]] |- | <nowiki>co</nowiki> || <nowiki>co</nowiki> || 2 || 440 || [[guh]], [[Misr]] |- | <nowiki>xbm</nowiki> || <nowiki>br</nowiki> || 2 || 2 || [[ez]], [[kûçik]] |- | <nowiki>shy</nowiki> || <nowiki>berberî</nowiki> || 2 || 22 || [[xanî]], [[goşt]] |- | <nowiki>nci</nowiki> || <nowiki>nah</nowiki> || 2 || 157 || [[kitêb]], [[bijang]] |- | <nowiki>dum</nowiki> || <nowiki>nl</nowiki> || 2 || 12 || [[xwarin]], [[kûçik]] |- | <nowiki>nds-nl</nowiki> || <nowiki>nds-de</nowiki> || 2 || 3 || [[bav]], [[bab]] |- | <nowiki>sjk</nowiki> || <nowiki>samî</nowiki> || 2 || 0 || [[bav]], [[bab]] |- | <nowiki>grc</nowiki> || <nowiki>yûnanî</nowiki> || 2 || 104 || [[kirîn]], [[çêkirin]] |- | <nowiki>zza</nowiki> || <nowiki>yua</nowiki> || 2 || 25 || [[kes]], [[kes]] |- | <nowiki>acp</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[gelek]], [[pir]] |- | <nowiki>gcf</nowiki> || <nowiki>gcf</nowiki> || 2 || 30 || [[Fransa]], [[Yûnanistan]] |- | <nowiki>kirîlî</nowiki> || <nowiki>serbo-croatian</nowiki> || 2 || 8 || [[DYA]], [[Budapeşt]] |- | <nowiki>roman</nowiki> || <nowiki>serbo-croatian</nowiki> || 2 || 1 || [[DYA]], [[Budapeşt]] |- | <nowiki>kirîlî</nowiki> || <nowiki>talysh</nowiki> || 2 || 8 || [[kûçik]], [[se/Werger]] |- | <nowiki>latînî</nowiki> || <nowiki>talysh</nowiki> || 2 || 5 || [[kûçik]], [[se/Werger]] |- | <nowiki>sdc</nowiki> || <nowiki>sardinian</nowiki> || 2 || 69 || [[heywan]], [[qehwe]] |- | <nowiki>mww</nowiki> || <nowiki>hmong</nowiki> || 2 || 318 || [[silav]], [[lîmon]] |- | <nowiki>pa</nowiki> || <nowiki>pa</nowiki> || 2 || 751 || [[saet]], [[hêştir]] |- | <nowiki>ug</nowiki> || <nowiki>ug</nowiki> || 2 || 1472 || [[şûştin]], [[şûştin]] |- | <nowiki>okm</nowiki> || <nowiki>ko</nowiki> || 2 || 34 || [[hirç]], [[japonî]] |- | <nowiki>nhv</nowiki> || <nowiki>nahuatl</nowiki> || 2 || 0 || [[hirç]], [[dar]] |- | <nowiki>mrq</nowiki> || <nowiki>marquesan</nowiki> || 2 || 5 || [[hesp]], [[esman]] |- | <nowiki>koi</nowiki> || <nowiki>komi</nowiki> || 2 || 180 || [[hesp]], [[bijang]] |- | <nowiki>kpv</nowiki> || <nowiki>komi</nowiki> || 2 || 131 || [[hesp]], [[bijang]] |- | <nowiki>ko</nowiki> || <nowiki>ko</nowiki> || 2 || 3919 || [[hêsir]], [[rondik]] |- | <nowiki>klasîk</nowiki> || <nowiki>nah</nowiki> || 2 || 0 || [[hêsir]], [[rondik]] |- | <nowiki>rûmî</nowiki> || <nowiki>malayî</nowiki> || 2 || 0 || [[rondik]], [[pembû]] |- | <nowiki>mns-nor</nowiki> || <nowiki>mansî</nowiki> || 2 || 83 || [[masî]], [[gol]] |- | <nowiki>nhe</nowiki> || <nowiki>nahuatl</nowiki> || 2 || 3 || [[mar]], [[giya]] |- | <nowiki>misrî</nowiki> || <nowiki>ar</nowiki> || 2 || 3 || [[kitik]], [[pisîk]] |- | <nowiki>libyen</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[kitik]], [[pisîk]] |- | <nowiki>marocain</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[kitik]], [[pisîk]] |- | <nowiki>lcp</nowiki> || <nowiki>lawa</nowiki> || 2 || 1 || [[agir]], [[xwê]] |- | <nowiki>pga</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[giya]], [[bîre]] |- | <nowiki>mey</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[dar]], [[kirm]] |- | <nowiki>nl</nowiki> || <nowiki>nl</nowiki> || 2 || 7038 || [[heke]], [[eger]] |- | <nowiki>sga</nowiki> || <nowiki>irish</nowiki> || 2 || 35 || [[afirandin]], [[rahib]] |- | <nowiki>atv</nowiki> || <nowiki>altai</nowiki> || 2 || 17 || [[guneh]], [[Qazaxistan]] |- | <nowiki>alt</nowiki> || <nowiki>altai</nowiki> || 2 || 368 || [[guneh]], [[Qazaxistan]] |- | <nowiki>mrj</nowiki> || <nowiki>mari</nowiki> || 2 || 1 || [[derya]], [[zêr]] |- | <nowiki>la</nowiki> || <nowiki>la</nowiki> || 2 || 3642 || [[kom]], [[fêris]] |- | <nowiki>rumî</nowiki> || <nowiki>ms</nowiki> || 2 || 0 || [[ax]], [[ay]] |- | <nowiki>pah</nowiki> || <nowiki>tenharim</nowiki> || 2 || 2 || [[term]], [[term]] |- | <nowiki>alupec</nowiki> || <nowiki>kabuverdianu</nowiki> || 2 || 0 || [[mejî]], [[berîlyûm]] |- | <nowiki>badiu</nowiki> || <nowiki>kabuverdianu</nowiki> || 2 || 0 || [[mejî]], [[berîlyûm]] |- | <nowiki>são vicente</nowiki> || <nowiki>kabuverdianu</nowiki> || 2 || 0 || [[mejî]], [[berîlyûm]] |- | <nowiki>yue</nowiki> || <nowiki>çînî</nowiki> || 2 || 27 || [[navdêr]], [[se/Werger]] |- | <nowiki>cmn</nowiki> || <nowiki>çînî</nowiki> || 2 || 55 || [[navdêr]], [[se/Werger]] |- | <nowiki>nan</nowiki> || <nowiki>çînî</nowiki> || 2 || 37 || [[navdêr]], [[se/Werger]] |- | <nowiki>roa-oca</nowiki> || <nowiki>ca</nowiki> || 2 || 1 || [[muzîk]], [[fîl]] |- | <nowiki>nso</nowiki> || <nowiki>st</nowiki> || 2 || 81 || [[piling]], [[leopar]] |- | <nowiki>ayp</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[elok]], [[kevçî]] |- | <nowiki>erebiya başûrê lewantê</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[belem]], [[fisteq]] |- | <nowiki>mrj</nowiki> || <nowiki>mr</nowiki> || 2 || 1 || [[zend]], [[bazin]] |- | <nowiki>rom</nowiki> || <nowiki>rom</nowiki> || 2 || 454 || [[penîr]], [[penîr]] |- | <nowiki>taywanî</nowiki> || <nowiki>{{zh}}</nowiki> || 2 || 0 || [[tebîet]], [[nexçer]] |- | <nowiki>xaa</nowiki> || <nowiki>ar</nowiki> || 2 || 1 || [[sîxur]], [[mişmiş]] |- | <nowiki>kea</nowiki> || <nowiki>kabuverdianu</nowiki> || 2 || 222 || [[sibat]], [[sibat]] |- | <nowiki>dsb</nowiki> || <nowiki>sorbian</nowiki> || 2 || 85 || [[hirmî]], [[hirmî]] |- | <nowiki>nhm</nowiki> || <nowiki>nahuatl</nowiki> || 2 || 0 || [[pelatînk]], [[dûpişk]] |- | <nowiki>nhi</nowiki> || <nowiki>nahuatl</nowiki> || 2 || 0 || [[pelatînk]], [[dûpişk]] |- | <nowiki>ayl</nowiki> || <nowiki>ar</nowiki> || 2 || 0 || [[kevçî]], [[gêzer]] |- | <nowiki>fa-ira</nowiki> || <nowiki>farisî</nowiki> || 2 || 0 || [[cins]], [[cins]] |- | <nowiki>sgs</nowiki> || <nowiki>lt</nowiki> || 2 || 145 || [[basketbol]], [[kişik]] |- | <nowiki>hangul</nowiki> || <nowiki>ko</nowiki> || 2 || 0 || [[femînîzm]], [[daxbar]] |- | <nowiki>romaja</nowiki> || <nowiki>ko</nowiki> || 2 || 0 || [[femînîzm]], [[daxbar]] |- | <nowiki>kirîlî</nowiki> || <nowiki>ru</nowiki> || 2 || 8 || [[femînîzm]], [[semasî]] |- | <nowiki>kanji</nowiki> || <nowiki>ja</nowiki> || 2 || 0 || [[bikeser]], [[daxbar]] |- | <nowiki>hiragana</nowiki> || <nowiki>ja</nowiki> || 2 || 0 || [[bikeser]], [[daxbar]] |- | <nowiki>crh</nowiki> || <nowiki>tt</nowiki> || 2 || 686 || [[gerestêr]], [[gerstêrk]] |- | <nowiki>yue</nowiki> || <nowiki>{{zh}}</nowiki> || 2 || 27 || [[tenikî]], [[Perwîn]] |- | <nowiki>cmn</nowiki> || <nowiki>{{zh}}</nowiki> || 2 || 55 || [[tenikî]], [[Perwîn]] |- | <nowiki>ja</nowiki> || <nowiki>ja</nowiki> || 2 || 4961 || [[sîsirk]], [[sîsirk]] |- | <nowiki>bjn</nowiki> || <nowiki>id</nowiki> || 2 || 69 || [[zov]], [[zîvzîve]] |- | <nowiki>mînankwayî</nowiki> || <nowiki>id</nowiki> || 2 || 0 || [[zov]], [[zîvzîve]] |- | <nowiki>sundanî</nowiki> || <nowiki>id</nowiki> || 2 || 9 || [[zov]], [[zîvzîve]] |- | <nowiki>bokmål</nowiki> || <nowiki>norwecî</nowiki> || 2 || 0 || [[bazirganiya azad]], [[avmêwe]] |- | <nowiki>nynorsk</nowiki> || <nowiki>norwecî</nowiki> || 2 || 0 || [[bazirganiya azad]], [[avmêwe]] |- | <nowiki>latînî</nowiki> || <nowiki>uz</nowiki> || 2 || 5 || [[plutokrasî]], [[onkolojî]] |- | <nowiki>gan</nowiki> || <nowiki>çînî</nowiki> || 2 || 0 || [[av/Werger]], [[se/Werger]] |- | <nowiki>adlamî</nowiki> || <nowiki>fulahî</nowiki> || 2 || 0 || [[se/Werger]], [[av/Werger1]] |- | <nowiki>latînî</nowiki> || <nowiki>fulahî</nowiki> || 2 || 5 || [[se/Werger]], [[av/Werger1]] |- | <nowiki>glagolîtîk</nowiki> || <nowiki>slaviya kevn</nowiki> || 2 || 0 || [[se/Werger]], [[av/Werger3]] |- | <nowiki>tlamacazapa nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 2 || 0 || [[av/Werger2]], [[av/Werger2]] |- | <nowiki>sardînî</nowiki> || <nowiki>sardînî</nowiki> || 2 || 17 || [[av/Werger3]], [[av/Werger3]] |- | <nowiki>erebiya bahranî</nowiki> || <nowiki>erebî</nowiki> || 2 || 0 || [[av/Werger1]], [[av/Werger1]] |- | <nowiki>erebiya ummanî</nowiki> || <nowiki>erebî</nowiki> || 2 || 0 || [[av/Werger1]], [[av/Werger1]] |- | <nowiki>keşmîrî</nowiki> || <nowiki>kashmiri</nowiki> || 2 || 3 || [[av/Werger1]], [[av/Werger1]] |- | <nowiki></nowiki> || <nowiki>sh</nowiki> || 2 || 0 || [[Îsayê Mesîh]], [[Îsayê Mesîh]] |- | <nowiki>qwm</nowiki> || <nowiki>kipchak</nowiki> || 1 || 0 || [[mişk]] |- | <nowiki>klasîk</nowiki> || <nowiki>nahwatlî</nowiki> || 1 || 0 || [[jin]] |- | <nowiki>nhx</nowiki> || <nowiki>nahwatlî</nowiki> || 1 || 2 || [[jin]] |- | <nowiki>ncj</nowiki> || <nowiki>nahwatlî</nowiki> || 1 || 0 || [[jin]] |- | <nowiki>el</nowiki> || <nowiki>xyy</nowiki> || 1 || 4553 || [[jin]] |- | <nowiki>grc</nowiki> || <nowiki>xyy</nowiki> || 1 || 104 || [[jin]] |- | <nowiki>mid</nowiki> || <nowiki>arc</nowiki> || 1 || 3 || [[mêr]] |- | <nowiki>mg</nowiki> || <nowiki>mg</nowiki> || 1 || 384 || [[îro]] |- | <nowiki>mn</nowiki> || <nowiki>zh</nowiki> || 1 || 1927 || [[bîst]] |- | <nowiki>ule</nowiki> || <nowiki>samî</nowiki> || 1 || 0 || [[duşem]] |- | <nowiki>latin</nowiki> || <nowiki>jv</nowiki> || 1 || 4 || [[ferheng]] |- | <nowiki>rml</nowiki> || <nowiki>romani</nowiki> || 1 || 0 || [[ferheng]] |- | <nowiki>yrk</nowiki> || <nowiki>nenets</nowiki> || 1 || 46 || [[ferheng]] |- | <nowiki>new</nowiki> || <nowiki>new</nowiki> || 1 || 75 || [[ferheng]] |- | <nowiki>shi</nowiki> || <nowiki>shi</nowiki> || 1 || 66 || [[ferheng]] |- | <nowiki>shu</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[mirov]] |- | <nowiki>kirîlî</nowiki> || <nowiki>gagauz</nowiki> || 1 || 8 || [[kurdî]] |- | <nowiki>latînî</nowiki> || <nowiki>gagauz</nowiki> || 1 || 5 || [[kurdî]] |- | <nowiki>kirîlî</nowiki> || <nowiki>turkmen</nowiki> || 1 || 8 || [[kurdî]] |- | <nowiki>latînî</nowiki> || <nowiki>turkmen</nowiki> || 1 || 5 || [[kurdî]] |- | <nowiki>cja</nowiki> || <nowiki>çamî</nowiki> || 1 || 9 || [[sor]] |- | <nowiki>csi</nowiki> || <nowiki>miwok</nowiki> || 1 || 0 || [[yek]] |- | <nowiki>lmw</nowiki> || <nowiki>miwok</nowiki> || 1 || 3 || [[yek]] |- | <nowiki>aramiya babîlonî ya cihûyî</nowiki> || <nowiki>aramî</nowiki> || 1 || 0 || [[roj]] |- | <nowiki>frîsiya bakur</nowiki> || <nowiki>frîsî</nowiki> || 1 || 1 || [[roj]] |- | <nowiki>prusîya kevn</nowiki> || <nowiki>frîsî</nowiki> || 1 || 2 || [[roj]] |- | <nowiki>frîsî</nowiki> || <nowiki>frîsî</nowiki> || 1 || 3 || [[roj]] |- | <nowiki>omx</nowiki> || <nowiki>mnw</nowiki> || 1 || 0 || [[çav]] |- | <nowiki>roa-gal</nowiki> || <nowiki>fr</nowiki> || 1 || 36 || [[dev]] |- | <nowiki>keçwayiya cankayê</nowiki> || <nowiki>qu</nowiki> || 1 || 0 || [[dev]] |- | <nowiki>keçwayiya wankayê</nowiki> || <nowiki>qu</nowiki> || 1 || 0 || [[dev]] |- | <nowiki>keçwayiya watwaşê</nowiki> || <nowiki>qu</nowiki> || 1 || 0 || [[dev]] |- | <nowiki>keçwayiya ekwadorî</nowiki> || <nowiki>qu</nowiki> || 1 || 0 || [[dev]] |- | <nowiki>keçwayiya başurî</nowiki> || <nowiki>qu</nowiki> || 1 || 0 || [[dev]] |- | <nowiki>he</nowiki> || <nowiki>lad</nowiki> || 1 || 3830 || [[dev]] |- | <nowiki>grc</nowiki> || <nowiki>zza</nowiki> || 1 || 104 || [[dev]] |- | <nowiki>grk-ita</nowiki> || <nowiki>zza</nowiki> || 1 || 9 || [[dev]] |- | <nowiki>cyrillic</nowiki> || <nowiki>mn</nowiki> || 1 || 1 || [[heft]] |- | <nowiki>bcl</nowiki> || <nowiki>bcl</nowiki> || 1 || 436 || [[zehmet]] |- | <nowiki>nci</nowiki> || <nowiki>arc</nowiki> || 1 || 157 || [[zevî]] |- | <nowiki>tly</nowiki> || <nowiki>talîşî</nowiki> || 1 || 96 || [[zevî]] |- | <nowiki>kevn</nowiki> || <nowiki>zza</nowiki> || 1 || 0 || [[dîtin]] |- | <nowiki>lij</nowiki> || <nowiki>lij</nowiki> || 1 || 296 || [[guh]] |- | <nowiki>vmf</nowiki> || <nowiki>de</nowiki> || 1 || 6 || [[ez]] |- | <nowiki>fo</nowiki> || <nowiki>fo</nowiki> || 1 || 2798 || [[ez]] |- | <nowiki>tji</nowiki> || <nowiki>tujia</nowiki> || 1 || 0 || [[ez]] |- | <nowiki>rif</nowiki> || <nowiki>berberî</nowiki> || 1 || 258 || [[xanî]] |- | <nowiki>kevn</nowiki> || <nowiki>he</nowiki> || 1 || 0 || [[û]] |- | <nowiki>min nan</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[ev]] |- | <nowiki>tly</nowiki> || <nowiki>th</nowiki> || 1 || 96 || [[ev]] |- | <nowiki>obt</nowiki> || <nowiki>br</nowiki> || 1 || 3 || [[ba]] |- | <nowiki>lrc</nowiki> || <nowiki>luri</nowiki> || 1 || 5 || [[welat]] |- | <nowiki>sga</nowiki> || <nowiki>îrlandî</nowiki> || 1 || 35 || [[hebûn]] |- | <nowiki>inc-ash</nowiki> || <nowiki>prakrit</nowiki> || 1 || 3 || [[hûn]] |- | <nowiki>literary</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[ew]] |- | <nowiki>lv</nowiki> || <nowiki>la</nowiki> || 1 || 2600 || [[zeman]] |- | <nowiki>latînî</nowiki> || <nowiki>tg</nowiki> || 1 || 5 || [[Sûriye]] |- | <nowiki>serdema navîn</nowiki> || <nowiki>el</nowiki> || 1 || 0 || [[Azerbeycan]] |- | <nowiki>xsa</nowiki> || <nowiki>old south arabian</nowiki> || 1 || 4 || [[Hebeşistan]] |- | <nowiki>adlamî</nowiki> || <nowiki>pulaar</nowiki> || 1 || 0 || [[Senegal]] |- | <nowiki>latînî</nowiki> || <nowiki>pulaar</nowiki> || 1 || 5 || [[Senegal]] |- | <nowiki>{{cmn}}</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[DYA]] |- | <nowiki>bohaîrîk</nowiki> || <nowiki>cop</nowiki> || 1 || 0 || [[ser]] |- | <nowiki>sahîdîk</nowiki> || <nowiki>cop</nowiki> || 1 || 0 || [[ser]] |- | <nowiki>fa-cls</nowiki> || <nowiki>fa</nowiki> || 1 || 0 || [[Enqere]] |- | <nowiki>zhx-tai</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[kûçik]] |- | <nowiki>ojw</nowiki> || <nowiki>oj</nowiki> || 1 || 6 || [[kûçik]] |- | <nowiki>egyptian arabic</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[xwastin]] |- | <nowiki>sdn</nowiki> || <nowiki>sc</nowiki> || 1 || 73 || [[ramûsîn]] |- | <nowiki>gmw-stm</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[sal]] |- | <nowiki>mk</nowiki> || <nowiki>mk</nowiki> || 1 || 2739 || [[gû]] |- | <nowiki>siryanî</nowiki> || <nowiki>el</nowiki> || 1 || 0 || [[Nîl]] |- | <nowiki>hnj</nowiki> || <nowiki>hmong</nowiki> || 1 || 8 || [[silav]] |- | <nowiki>latînî</nowiki> || <nowiki>azerî</nowiki> || 1 || 5 || [[telefon]] |- | <nowiki>erebî</nowiki> || <nowiki>azerî</nowiki> || 1 || 4 || [[telefon]] |- | <nowiki>tzo</nowiki> || <nowiki>tzo</nowiki> || 1 || 41 || [[saet]] |- | <nowiki>lt</nowiki> || <nowiki>lithuanian</nowiki> || 1 || 2754 || [[cografya]] |- | <nowiki>sgs</nowiki> || <nowiki>lithuanian</nowiki> || 1 || 145 || [[cografya]] |- | <nowiki>yok-svy</nowiki> || <nowiki>yokutî</nowiki> || 1 || 1 || [[gur]] |- | <nowiki>sne</nowiki> || <nowiki>bidayuh</nowiki> || 1 || 99 || [[hirç]] |- | <nowiki>sdo</nowiki> || <nowiki>bidayuh</nowiki> || 1 || 5 || [[hirç]] |- | <nowiki>bth</nowiki> || <nowiki>bidayuh</nowiki> || 1 || 21 || [[hirç]] |- | <nowiki>crl</nowiki> || <nowiki>cr</nowiki> || 1 || 6 || [[hirç]] |- | <nowiki>cr</nowiki> || <nowiki>cr</nowiki> || 1 || 113 || [[hirç]] |- | <nowiki>crj</nowiki> || <nowiki>cr</nowiki> || 1 || 4 || [[hirç]] |- | <nowiki>apl</nowiki> || <nowiki>apaçî</nowiki> || 1 || 0 || [[hesp]] |- | <nowiki>apk</nowiki> || <nowiki>apaçî</nowiki> || 1 || 6 || [[hesp]] |- | <nowiki>klasîk</nowiki> || <nowiki>fa</nowiki> || 1 || 0 || [[hesp]] |- | <nowiki>mqm</nowiki> || <nowiki>marquesan</nowiki> || 1 || 6 || [[hesp]] |- | <nowiki>psy</nowiki> || <nowiki>nanticoke</nowiki> || 1 || 0 || [[hesp]] |- | <nowiki>nds</nowiki> || <nowiki>de</nowiki> || 1 || 736 || [[lal]] |- | <nowiki>erebî</nowiki> || <nowiki>malayî</nowiki> || 1 || 4 || [[rondik]] |- | <nowiki>mns-cen</nowiki> || <nowiki>mansî</nowiki> || 1 || 0 || [[masî]] |- | <nowiki>psy</nowiki> || <nowiki>nnt</nowiki> || 1 || 0 || [[masî]] |- | <nowiki>id</nowiki> || <nowiki>ms</nowiki> || 1 || 2910 || [[siberoj]] |- | <nowiki>abd</nowiki> || <nowiki>ar</nowiki> || 1 || 1 || [[sibe]] |- | <nowiki>bfs</nowiki> || <nowiki>bai</nowiki> || 1 || 1 || [[agir]] |- | <nowiki>nhh</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[agir]] |- | <nowiki>arz</nowiki> || <nowiki>ary</nowiki> || 1 || 20 || [[kursî]] |- | <nowiki>arc</nowiki> || <nowiki>ar</nowiki> || 1 || 480 || [[pişt]] |- | <nowiki>min</nowiki> || <nowiki>mhr</nowiki> || 1 || 129 || [[mezin]] |- | <nowiki>yi</nowiki> || <nowiki>de</nowiki> || 1 || 1877 || [[guneh]] |- | <nowiki>ang</nowiki> || <nowiki>english</nowiki> || 1 || 1668 || [[guneh]] |- | <nowiki>îbranî</nowiki> || <nowiki>he</nowiki> || 1 || 4 || [[dawî]] |- | <nowiki>latînî</nowiki> || <nowiki>he</nowiki> || 1 || 5 || [[dawî]] |- | <nowiki>föhr-amrum</nowiki> || <nowiki>frr</nowiki> || 1 || 0 || [[kelîme]] |- | <nowiki>helgoland</nowiki> || <nowiki>frr</nowiki> || 1 || 0 || [[kelîme]] |- | <nowiki>sylt</nowiki> || <nowiki>frr</nowiki> || 1 || 0 || [[kelîme]] |- | <nowiki>gun</nowiki> || <nowiki>gn</nowiki> || 1 || 126 || [[term]] |- | <nowiki>tpj</nowiki> || <nowiki>gn</nowiki> || 1 || 0 || [[term]] |- | <nowiki>zai</nowiki> || <nowiki>zapotec</nowiki> || 1 || 28 || [[term]] |- | <nowiki>{{arz}}</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[bar]] |- | <nowiki>gsw</nowiki> || <nowiki>gl</nowiki> || 1 || 93 || [[bar]] |- | <nowiki>de</nowiki> || <nowiki>sorbî</nowiki> || 1 || 21709 || [[girtin]] |- | <nowiki>la</nowiki> || <nowiki>latin</nowiki> || 1 || 3642 || [[gore]] |- | <nowiki>kirîlî</nowiki> || <nowiki>serbo-{{hr}}</nowiki> || 1 || 8 || [[dizî]] |- | <nowiki>roman</nowiki> || <nowiki>serbo-{{hr}}</nowiki> || 1 || 1 || [[dizî]] |- | <nowiki>rmn</nowiki> || <nowiki>romanî</nowiki> || 1 || 0 || [[hesan]] |- | <nowiki>(''sade'')</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[firoke]] |- | <nowiki>(''kevnare'')</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[firoke]] |- | <nowiki>scn</nowiki> || <nowiki>it</nowiki> || 1 || 1058 || [[helîkopter]] |- | <nowiki>hif</nowiki> || <nowiki>hi</nowiki> || 1 || 31 || [[rêziman]] |- | <nowiki>maroko</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[bestenî]] |- | <nowiki>slm</nowiki> || <nowiki>sama</nowiki> || 1 || 3 || [[hêk]] |- | <nowiki>owl</nowiki> || <nowiki>cy</nowiki> || 1 || 1 || [[hêk]] |- | <nowiki>kevn</nowiki> || <nowiki>got</nowiki> || 1 || 0 || [[red kirin]] |- | <nowiki>apm</nowiki> || <nowiki>apk</nowiki> || 1 || 9 || [[afret]] |- | <nowiki>apj</nowiki> || <nowiki>apk</nowiki> || 1 || 10 || [[afret]] |- | <nowiki>apaçiya rojava</nowiki> || <nowiki>apk</nowiki> || 1 || 0 || [[afret]] |- | <nowiki>krîlî</nowiki> || <nowiki>cu</nowiki> || 1 || 0 || [[dê]] |- | <nowiki>glagolîtîkî</nowiki> || <nowiki>cu</nowiki> || 1 || 0 || [[dê]] |- | <nowiki>{{w|arz}}</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[dê]] |- | <nowiki>* [[taywanî]]</nowiki> || <nowiki>{{zh}}</nowiki> || 1 || 0 || [[xwerist]] |- | <nowiki>kevn</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[lawaz]] |- | <nowiki>pne</nowiki> || <nowiki>penan</nowiki> || 1 || 1 || [[piling]] |- | <nowiki>pez</nowiki> || <nowiki>penan</nowiki> || 1 || 1 || [[piling]] |- | <nowiki>pra-ard</nowiki> || <nowiki>prakrîtî</nowiki> || 1 || 0 || [[piling]] |- | <nowiki>mns-sou</nowiki> || <nowiki>mansî</nowiki> || 1 || 0 || [[gol]] |- | <nowiki>pa</nowiki> || <nowiki>gurmukhi</nowiki> || 1 || 751 || [[xaîn]] |- | <nowiki>aer</nowiki> || <nowiki>arrernte</nowiki> || 1 || 58 || [[mûrî]] |- | <nowiki>are</nowiki> || <nowiki>arrernte</nowiki> || 1 || 1 || [[mûrî]] |- | <nowiki>sq</nowiki> || <nowiki>albanian</nowiki> || 1 || 2799 || [[kelandin]] |- | <nowiki>{{he}}</nowiki> || <nowiki>arc</nowiki> || 1 || 0 || [[cam]] |- | <nowiki>rmf</nowiki> || <nowiki>romanî</nowiki> || 1 || 0 || [[qelem]] |- | <nowiki>latînî</nowiki> || <nowiki>ms</nowiki> || 1 || 5 || [[tajî]] |- | <nowiki>ar</nowiki> || <nowiki>ms</nowiki> || 1 || 11779 || [[tajî]] |- | <nowiki><hiero>n</nowiki> || <nowiki>egy</nowiki> || 1 || 0 || [[text]] |- | <nowiki>cu</nowiki> || <nowiki>cu</nowiki> || 1 || 615 || [[gewrî]] |- | <nowiki>dsb</nowiki> || <nowiki>wen</nowiki> || 1 || 85 || [[gewrî]] |- | <nowiki>hsb</nowiki> || <nowiki>wen</nowiki> || 1 || 110 || [[gewrî]] |- | <nowiki>ang</nowiki> || <nowiki>it</nowiki> || 1 || 1668 || [[pêjan]] |- | <nowiki>sq</nowiki> || <nowiki>ak</nowiki> || 1 || 2799 || [[spas]] |- | <nowiki>tts</nowiki> || <nowiki>th</nowiki> || 1 || 20 || [[spas]] |- | <nowiki>ky</nowiki> || <nowiki>ky</nowiki> || 1 || 1800 || [[tov]] |- | <nowiki>nch</nowiki> || <nowiki>nahwatlî</nowiki> || 1 || 2 || [[nîvişk]] |- | <nowiki>nhn</nowiki> || <nowiki>nahwatlî</nowiki> || 1 || 6 || [[nîvişk]] |- | <nowiki>nhe</nowiki> || <nowiki>nahwatlî</nowiki> || 1 || 3 || [[nîvişk]] |- | <nowiki>egl</nowiki> || <nowiki>emilian</nowiki> || 1 || 80 || [[bijang]] |- | <nowiki>crh</nowiki> || <nowiki>cr</nowiki> || 1 || 686 || [[bijang]] |- | <nowiki>rif</nowiki> || <nowiki>rif</nowiki> || 1 || 258 || [[bijang]] |- | <nowiki>latînî</nowiki> || <nowiki>tt</nowiki> || 1 || 5 || [[bijang]] |- | <nowiki>sco</nowiki> || <nowiki>sco</nowiki> || 1 || 770 || [[enîşk]] |- | <nowiki>kevn</nowiki> || <nowiki>grc</nowiki> || 1 || 0 || [[girê dan]] |- | <nowiki>erebiya bakurê lewantê</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[belem]] |- | <nowiki>be</nowiki> || <nowiki>be</nowiki> || 1 || 2638 || [[îngilîz]] |- | <nowiki>cmn</nowiki> || <nowiki>en</nowiki> || 1 || 55 || [[kul]] |- | <nowiki>kirîlî</nowiki> || <nowiki>judeo-tat</nowiki> || 1 || 8 || [[zêr]] |- | <nowiki>îbranî</nowiki> || <nowiki>judeo-tat</nowiki> || 1 || 4 || [[zêr]] |- | <nowiki>jdt</nowiki> || <nowiki>judeo-tat</nowiki> || 1 || 10 || [[zêr]] |- | <nowiki>scientific latinized genera</nowiki> || <nowiki>la</nowiki> || 1 || 0 || [[qaz]] |- | <nowiki>pdt</nowiki> || <nowiki>nds</nowiki> || 1 || 840 || [[qaz]] |- | <nowiki>pi</nowiki> || <nowiki>os</nowiki> || 1 || 198 || [[qaz]] |- | <nowiki>mrj</nowiki> || <nowiki>mhr</nowiki> || 1 || 1 || [[werdek]] |- | <nowiki>cop</nowiki> || <nowiki>egy</nowiki> || 1 || 322 || [[hinar]] |- | <nowiki>latînî</nowiki> || <nowiki>ug</nowiki> || 1 || 5 || [[çakûç]] |- | <nowiki>griko</nowiki> || <nowiki>el</nowiki> || 1 || 0 || [[çakûç]] |- | <nowiki>amu</nowiki> || <nowiki>amuzgo</nowiki> || 1 || 42 || [[qehwe]] |- | <nowiki>azg</nowiki> || <nowiki>amuzgo</nowiki> || 1 || 3 || [[qehwe]] |- | <nowiki>poi</nowiki> || <nowiki>popoluca</nowiki> || 1 || 1 || [[qehwe]] |- | <nowiki>plo</nowiki> || <nowiki>popoluca</nowiki> || 1 || 0 || [[qehwe]] |- | <nowiki>pos</nowiki> || <nowiki>popoluca</nowiki> || 1 || 1 || [[qehwe]] |- | <nowiki>swb</nowiki> || <nowiki>zdj</nowiki> || 1 || 142 || [[qehwe]] |- | <nowiki>zoc</nowiki> || <nowiki>zoque</nowiki> || 1 || 3 || [[qehwe]] |- | <nowiki>zor</nowiki> || <nowiki>zoque</nowiki> || 1 || 0 || [[qehwe]] |- | <nowiki>nb</nowiki> || <nowiki>nrf</nowiki> || 1 || 88 || [[hewş]] |- | <nowiki>ckb</nowiki> || <nowiki>kurdî</nowiki> || 1 || 61 || [[penîr]] |- | <nowiki>sdh</nowiki> || <nowiki>kurdî</nowiki> || 1 || 5 || [[penîr]] |- | <nowiki>zza</nowiki> || <nowiki>kurdî</nowiki> || 1 || 25 || [[penîr]] |- | <nowiki>alupec</nowiki> || <nowiki>kea</nowiki> || 1 || 0 || [[tebax]] |- | <nowiki>badiu</nowiki> || <nowiki>kea</nowiki> || 1 || 0 || [[tebax]] |- | <nowiki>são vicente</nowiki> || <nowiki>kea</nowiki> || 1 || 0 || [[tebax]] |- | <nowiki>zun</nowiki> || <nowiki>esu</nowiki> || 1 || 37 || [[tebax]] |- | <nowiki>pinyînî</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[melûl]] |- | <nowiki>kanjî</nowiki> || <nowiki>ja</nowiki> || 1 || 0 || [[melûl]] |- | <nowiki>hîraganayî</nowiki> || <nowiki>ja</nowiki> || 1 || 0 || [[melûl]] |- | <nowiki>romajî</nowiki> || <nowiki>ja</nowiki> || 1 || 0 || [[melûl]] |- | <nowiki>hangulî</nowiki> || <nowiki>{{ko}}</nowiki> || 1 || 0 || [[melûl]] |- | <nowiki>romajî</nowiki> || <nowiki>{{ko}}</nowiki> || 1 || 0 || [[melûl]] |- | <nowiki>erebî</nowiki> || <nowiki>ug</nowiki> || 1 || 4 || [[kirîlî]] |- | <nowiki>syc</nowiki> || <nowiki>sh</nowiki> || 1 || 32 || [[bilbil]] |- | <nowiki>middle high german / early new high german</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[hoker]] |- | <nowiki>apj</nowiki> || <nowiki>apache</nowiki> || 1 || 10 || [[hirmî]] |- | <nowiki>hsb</nowiki> || <nowiki>sorbian</nowiki> || 1 || 110 || [[hirmî]] |- | <nowiki>vls</nowiki> || <nowiki>flemish</nowiki> || 1 || 65 || [[sîr]] |- | <nowiki>oko</nowiki> || <nowiki>ko</nowiki> || 1 || 6 || [[japonî]] |- | <nowiki>ak</nowiki> || <nowiki>akan</nowiki> || 1 || 162 || [[pelatînk]] |- | <nowiki>nhk</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[pelatînk]] |- | <nowiki>ote</nowiki> || <nowiki>otomi</nowiki> || 1 || 13 || [[pelatînk]] |- | <nowiki>hebrew</nowiki> || <nowiki>arc</nowiki> || 1 || 0 || [[dûpişk]] |- | <nowiki>syriac</nowiki> || <nowiki>arc</nowiki> || 1 || 0 || [[dûpişk]] |- | <nowiki>iraqi arabic</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[pêşû]] |- | <nowiki>api</nowiki> || <nowiki>ar</nowiki> || 1 || 2 || [[pêşû]] |- | <nowiki>ms</nowiki> || <nowiki>malayî</nowiki> || 1 || 2601 || [[pembû]] |- | <nowiki>gml</nowiki> || <nowiki>nds-de</nowiki> || 1 || 15 || [[zimanê zikmakî]] |- | <nowiki>mandarînî</nowiki> || <nowiki>tr</nowiki> || 1 || 7 || [[cêwî]] |- | <nowiki>kirîlî</nowiki> || <nowiki>mongolî</nowiki> || 1 || 8 || [[çante]] |- | <nowiki>middle french</nowiki> || <nowiki>fr</nowiki> || 1 || 0 || [[birc]] |- | <nowiki>tuscan dialect</nowiki> || <nowiki>it</nowiki> || 1 || 0 || [[rûvî]] |- | <nowiki>kbd</nowiki> || <nowiki>çerkezî</nowiki> || 1 || 207 || [[Qefqasya]] |- | <nowiki>ady</nowiki> || <nowiki>çerkezî</nowiki> || 1 || 361 || [[Qefqasya]] |- | <nowiki>fa</nowiki> || <nowiki>hy</nowiki> || 1 || 16419 || [[gurz]] |- | <nowiki>traditional chinese</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[femînîzm]] |- | <nowiki>katakana</nowiki> || <nowiki>ja</nowiki> || 1 || 0 || [[femînîzm]] |- | <nowiki>roman</nowiki> || <nowiki>ru</nowiki> || 1 || 1 || [[femînîzm]] |- | <nowiki>tr</nowiki> || <nowiki>tr</nowiki> || 1 || 51197 || [[çinar]] |- | <nowiki>enm</nowiki> || <nowiki>ang</nowiki> || 1 || 408 || [[erzînk]] |- | <nowiki>kirîlî</nowiki> || <nowiki>karakalpak</nowiki> || 1 || 8 || [[erzînk]] |- | <nowiki>latînî</nowiki> || <nowiki>karakalpak</nowiki> || 1 || 5 || [[erzînk]] |- | <nowiki>misiriya navîn û dereng</nowiki> || <nowiki>egy</nowiki> || 1 || 0 || [[erzînk]] |- | <nowiki>misiriya kevn</nowiki> || <nowiki>egy</nowiki> || 1 || 0 || [[erzînk]] |- | <nowiki>l</nowiki> || <nowiki>sh</nowiki> || 1 || 0 || [[kîwî]] |- | <nowiki>hangul</nowiki> || <nowiki>{{ko}}</nowiki> || 1 || 0 || [[bikeser]] |- | <nowiki>romaja</nowiki> || <nowiki>{{ko}}</nowiki> || 1 || 0 || [[bikeser]] |- | <nowiki>krîlî</nowiki> || <nowiki>mn</nowiki> || 1 || 0 || [[Dalay-Lama]] |- | <nowiki>latînî</nowiki> || <nowiki>ru</nowiki> || 1 || 5 || [[semasî]] |- | <nowiki>badisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>berliner</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>bernese</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>camelottisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>frankonian</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>lichtensteinisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>luxembourgeois</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>moselfraenkisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>plattdeutsch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>rhoihessisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>ruhrisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>saarlaendisch</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>saxon</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>swabian</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>viennese</nowiki> || <nowiki>de</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>canadian french</nowiki> || <nowiki>fr</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>antwerps dialect</nowiki> || <nowiki>nl</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>bilzers dialect</nowiki> || <nowiki>nl</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>limburgs dialect</nowiki> || <nowiki>nl</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>spouwers dialect</nowiki> || <nowiki>nl</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>twents dialect</nowiki> || <nowiki>nl</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>(piemonte)</nowiki> || <nowiki>it</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>(romagna)</nowiki> || <nowiki>it</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>polite</nowiki> || <nowiki>ja</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>polite</nowiki> || <nowiki>{{ko}}</nowiki> || 1 || 0 || [[rojbûna te pîroz be]] |- | <nowiki>tunisian arabic</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[lîmon]] |- | <nowiki>grk-mar</nowiki> || <nowiki>el</nowiki> || 1 || 11 || [[lîmon]] |- | <nowiki>cim</nowiki> || <nowiki>bar</nowiki> || 1 || 42 || [[noel]] |- | <nowiki>mhn</nowiki> || <nowiki>bar</nowiki> || 1 || 225 || [[noel]] |- | <nowiki>darî</nowiki> || <nowiki>fa</nowiki> || 1 || 0 || [[asansor]] |- | <nowiki>norman french</nowiki> || <nowiki>fr</nowiki> || 1 || 0 || [[sandwîç]] |- | <nowiki>nûjen</nowiki> || <nowiki>el</nowiki> || 1 || 0 || [[klîtorîs]] |- | <nowiki>[[alupec]] or [[alupek]]</nowiki> || <nowiki>capeverdean crioulo</nowiki> || 1 || 0 || [[mejg]] |- | <nowiki>badiú</nowiki> || <nowiki>capeverdean crioulo</nowiki> || 1 || 0 || [[mejg]] |- | <nowiki>são vicente</nowiki> || <nowiki>capeverdean crioulo</nowiki> || 1 || 0 || [[mejg]] |- | <nowiki>ro</nowiki> || <nowiki>sh</nowiki> || 1 || 4209 || [[koma]] |- | <nowiki>cmn</nowiki> || <nowiki>ar</nowiki> || 1 || 55 || [[bizdandî]] |- | <nowiki>kantonî</nowiki> || <nowiki>zh</nowiki> || 1 || 6 || [[bêzehmet]] |- | <nowiki>flamî</nowiki> || <nowiki>nl</nowiki> || 1 || 0 || [[bêzehmet]] |- | <nowiki>{{yue}}</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[seksolojî]] |- | <nowiki>nan</nowiki> || <nowiki>{{zh}}</nowiki> || 1 || 37 || [[Perwîn]] |- | <nowiki>mandarînî</nowiki> || <nowiki>en</nowiki> || 1 || 7 || [[ajelnas]] |- | <nowiki>gurmuxî</nowiki> || <nowiki>punjabî</nowiki> || 1 || 0 || [[bêhnijîn]] |- | <nowiki>şahmuxî</nowiki> || <nowiki>punjabî</nowiki> || 1 || 0 || [[bêhnijîn]] |- | <nowiki>min</nowiki> || <nowiki>id</nowiki> || 1 || 129 || [[hezarpê]] |- | <nowiki>su</nowiki> || <nowiki>id</nowiki> || 1 || 373 || [[hezarpê]] |- | <nowiki>glagolî</nowiki> || <nowiki>cu</nowiki> || 1 || 0 || [[kotir]] |- | <nowiki>ya jorîn</nowiki> || <nowiki>sorbî</nowiki> || 1 || 0 || [[miravî]] |- | <nowiki>arbëresh</nowiki> || <nowiki>sq</nowiki> || 1 || 0 || [[çawa yî]] |- | <nowiki>tashelhit</nowiki> || <nowiki>ar</nowiki> || 1 || 0 || [[mejiyê hestiyan]] |- | <nowiki>{{ttbc|cmn}}</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[mejiyê hestiyan]] |- | <nowiki>glagolîtîk</nowiki> || <nowiki>mn</nowiki> || 1 || 0 || [[rahib]] |- | <nowiki>xcl</nowiki> || <nowiki>armenian</nowiki> || 1 || 6 || [[Hacer]] |- | <nowiki>yûnaniya kevn</nowiki> || <nowiki>el</nowiki> || 1 || 2 || [[îsfenc]] |- | <nowiki>bokmål</nowiki> || <nowiki>{{no}}</nowiki> || 1 || 0 || [[fîlmê tirsê]] |- | <nowiki>nynorsk</nowiki> || <nowiki>{{no}}</nowiki> || 1 || 0 || [[fîlmê tirsê]] |- | <nowiki>kirîlî</nowiki> || <nowiki>{{ru}}</nowiki> || 1 || 8 || [[metoste]] |- | <nowiki>roman</nowiki> || <nowiki>{{ru}}</nowiki> || 1 || 1 || [[metoste]] |- | <nowiki>kirîlî</nowiki> || <nowiki>sirbo-xirwatî</nowiki> || 1 || 8 || [[Cinêv]] |- | <nowiki>roman</nowiki> || <nowiki>sirbo-xirwatî</nowiki> || 1 || 1 || [[Cinêv]] |- | <nowiki>gsw</nowiki> || <nowiki>ka</nowiki> || 1 || 93 || [[noşî can be]] |- | <nowiki>cs</nowiki> || <nowiki>zh</nowiki> || 1 || 4550 || [[basûr]] |- | <nowiki>cantonese</nowiki> || <nowiki>zh</nowiki> || 1 || 0 || [[muslî]] |- | <nowiki>ûygurnî</nowiki> || <nowiki>mn</nowiki> || 1 || 0 || [[paldank]] |- | <nowiki>nb</nowiki> || <nowiki>mr</nowiki> || 1 || 88 || [[niqteşanî]] |- | <nowiki>erebî</nowiki> || <nowiki>kk</nowiki> || 1 || 4 || [[şerê psîkolojîk]] |- | <nowiki>kirîlî</nowiki> || <nowiki>kk</nowiki> || 1 || 8 || [[şerê psîkolojîk]] |- | <nowiki>elmanîşî</nowiki> || <nowiki>almanî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>bayerî</nowiki> || <nowiki>almanî</nowiki> || 1 || 1 || [[av/Werger]] |- | <nowiki>frankoniya navendî</nowiki> || <nowiki>almanî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>frankiya rojhilatî</nowiki> || <nowiki>almanî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>gottscheerish</nowiki> || <nowiki>almanî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>almaniya pennsylvania</nowiki> || <nowiki>almanî</nowiki> || 1 || 1 || [[av/Werger]] |- | <nowiki>frankoniya rhine</nowiki> || <nowiki>almanî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>swabî</nowiki> || <nowiki>almanî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>san pedro amuzgos amuzgo</nowiki> || <nowiki>amuzgo</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>amuzgoyî</nowiki> || <nowiki>amuzgo</nowiki> || 1 || 7 || [[av/Werger]] |- | <nowiki>îbranî</nowiki> || <nowiki>aramî</nowiki> || 1 || 4 || [[av/Werger]] |- | <nowiki>siryanî</nowiki> || <nowiki>aramî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>ozumacín chinantec</nowiki> || <nowiki>chinantec</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>tlacoatzintepec chinantec</nowiki> || <nowiki>chinantec</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>jin</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>min bei</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>xiang</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[av/Werger]] |- | <nowiki>gmh</nowiki> || <nowiki>almanî</nowiki> || 1 || 17 || [[se/Werger]] |- | <nowiki>goh</nowiki> || <nowiki>almanî</nowiki> || 1 || 68 || [[se/Werger]] |- | <nowiki>gsw</nowiki> || <nowiki>almanî</nowiki> || 1 || 93 || [[se/Werger]] |- | <nowiki>aii</nowiki> || <nowiki>aramî</nowiki> || 1 || 295 || [[se/Werger]] |- | <nowiki>syc</nowiki> || <nowiki>aramî</nowiki> || 1 || 32 || [[se/Werger]] |- | <nowiki>tmr</nowiki> || <nowiki>aramî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>xbm</nowiki> || <nowiki>bretonî</nowiki> || 1 || 2 || [[se/Werger]] |- | <nowiki>dng</nowiki> || <nowiki>çînî</nowiki> || 1 || 3 || [[se/Werger]] |- | <nowiki>hak</nowiki> || <nowiki>çînî</nowiki> || 1 || 1 || [[se/Werger]] |- | <nowiki>mnp</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>cdo</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>zhx-tai</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>wuu</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>hsn</nowiki> || <nowiki>çînî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>arz</nowiki> || <nowiki>erebî</nowiki> || 1 || 20 || [[se/Werger]] |- | <nowiki>afb</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>acw</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>acm</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>ary</nowiki> || <nowiki>erebî</nowiki> || 1 || 1 || [[se/Werger]] |- | <nowiki>ajp</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[se/Werger]] |- | <nowiki>pal</nowiki> || <nowiki>farisî</nowiki> || 1 || 83 || [[se/Werger]] |- | <nowiki>frm</nowiki> || <nowiki>fransî</nowiki> || 1 || 10 || [[se/Werger]] |- | <nowiki>fro</nowiki> || <nowiki>fransî</nowiki> || 1 || 110 || [[se/Werger]] |- | <nowiki>dum</nowiki> || <nowiki>holendî</nowiki> || 1 || 12 || [[se/Werger]] |- | <nowiki>sga</nowiki> || <nowiki>îrlendî</nowiki> || 1 || 35 || [[se/Werger]] |- | <nowiki>nds-de</nowiki> || <nowiki>nedersaksî</nowiki> || 1 || 6 || [[se/Werger]] |- | <nowiki>gml</nowiki> || <nowiki>nedersaksî</nowiki> || 1 || 15 || [[se/Werger]] |- | <nowiki>ojw</nowiki> || <nowiki>ojibweyî</nowiki> || 1 || 6 || [[se/Werger]] |- | <nowiki>sc</nowiki> || <nowiki>sardînî</nowiki> || 1 || 670 || [[se/Werger]] |- | <nowiki>wlm</nowiki> || <nowiki>weylsî</nowiki> || 1 || 2 || [[se/Werger]] |- | <nowiki>santiago</nowiki> || <nowiki>kabuverdianu</nowiki> || 1 || 0 || [[berîlyûm]] |- | <nowiki>malezî</nowiki> || <nowiki>malay</nowiki> || 1 || 15 || [[av/Werger2]] |- | <nowiki>mariya rojhilatî</nowiki> || <nowiki>marî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>western mari</nowiki> || <nowiki>marî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>central mazahua</nowiki> || <nowiki>mazahua</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>michoacán mazahua</nowiki> || <nowiki>mazahua</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>san jerónimo tecóatl mazatec</nowiki> || <nowiki>mazatec</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>chiquihuitlán mazatec</nowiki> || <nowiki>mazatec</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>akatepekiya me'phaa</nowiki> || <nowiki>me'phaa</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>azoyú me'phaa</nowiki> || <nowiki>me'phaa</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>tlacoapa me'phaa</nowiki> || <nowiki>me'phaa</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>ayutla mixtec</nowiki> || <nowiki>mixtec</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>san miguel el grande mixtec</nowiki> || <nowiki>mixtec</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>tezoatlán mixtec</nowiki> || <nowiki>mixtec</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>yosondúa mixtec</nowiki> || <nowiki>mixtec</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>mongoliya klasîk</nowiki> || <nowiki>mongolî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>mongolî</nowiki> || <nowiki>mongolî</nowiki> || 1 || 6 || [[av/Werger2]] |- | <nowiki>central huasteca nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>coatepec nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>durango nahuatliya rojavayî</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>eastern huasteca nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>guerrero nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>puebla nahuatliya çiyayî</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>huaxcaleca nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>nahuatliya morelos</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>nahuatliya mekayapan</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>orizaba nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>tabasco nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>temascaltepec nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>tetelcingo nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>durango nahuatliya rojavayî ya jêrîn</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>western huasteca nahuatl</nowiki> || <nowiki>nahuatl</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>saksoniya jêrîn ya holendî</nowiki> || <nowiki>nedersaksî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>almaniya jêrîn a almanî</nowiki> || <nowiki>nedersaksî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>norweciya bokmålî</nowiki> || <nowiki>norwecî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>norweciya nînorskî</nowiki> || <nowiki>norwecî</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>estado de méxico otomi</nowiki> || <nowiki>otomi</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>mezquital otomi</nowiki> || <nowiki>otomi</nowiki> || 1 || 1 || [[av/Werger2]] |- | <nowiki>temoaya otomi</nowiki> || <nowiki>otomi</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>san juan atzingo popoloca</nowiki> || <nowiki>popoloca</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>highland popoluca</nowiki> || <nowiki>popoluca</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>sayula popoluca</nowiki> || <nowiki>popoluca</nowiki> || 1 || 0 || [[av/Werger2]] |- | <nowiki>bohaîrî</nowiki> || <nowiki>qiptî</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>sahîdî</nowiki> || <nowiki>qiptî</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>sinte romani</nowiki> || <nowiki>romanî</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>kirîlî</nowiki> || <nowiki>romanyayî</nowiki> || 1 || 8 || [[av/Werger3]] |- | <nowiki>sirboxirwatî</nowiki> || <nowiki>sirboxirwatî</nowiki> || 1 || 7 || [[av/Werger3]] |- | <nowiki>spaniya kevn</nowiki> || <nowiki>spanî</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>talişî</nowiki> || <nowiki>talysh</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>teke-tege</nowiki> || <nowiki>teke</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>nzadi</nowiki> || <nowiki>teke</nowiki> || 1 || 1 || [[av/Werger3]] |- | <nowiki>teke-tyee</nowiki> || <nowiki>teke</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>huehuetla tepehua</nowiki> || <nowiki>tepehua</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>tlachichilco tepehua</nowiki> || <nowiki>tepehua</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>highland totonac</nowiki> || <nowiki>totonac</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>misantla totonac</nowiki> || <nowiki>totonac</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>papantla totonac</nowiki> || <nowiki>totonac</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>yokutiya geliyê bakurî</nowiki> || <nowiki>yokutî</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>yokutiya geliyê başûrî</nowiki> || <nowiki>yokutî</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>aloápam zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>amatlán zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>ayoquesco zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>choapan zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>coatlán zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>cajonos zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>central mahuatlán zapoteco</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>chichicapan zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>guevea de humboldt zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>îsthmus zapotekî</nowiki> || <nowiki>zapotec</nowiki> || 1 || 1 || [[av/Werger3]] |- | <nowiki>lachixío zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>mitla zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>mixtepec zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>ocotlán zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>ozolotepec zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>san baltazar loxicha zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>san juan guelavía zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>san pedro quiatoni zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>san vicente coatlán zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>santa inés yatzechi zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>santa maría quiegolani zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>santiago xanica zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>santo domingo albarradas zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>tabaa zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>texmelucan zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>tlacolulita zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>zapotekiya yalalagî</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>yareni zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>yatee zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>yatzachi zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>zaniza zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>zoogocho zapotec</nowiki> || <nowiki>zapotec</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>chimalapa zoque</nowiki> || <nowiki>zoque</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>copainalá zoque</nowiki> || <nowiki>zoque</nowiki> || 1 || 0 || [[av/Werger3]] |- | <nowiki>erebiya cezayirî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya endulusî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>chadian arabic</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya kibrisî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>hassaniya</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya iraqî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya bakûrê şamê</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya lîbyayî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya necdî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya başûrê şamê</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>erebiya sudanî</nowiki> || <nowiki>erebî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>fransiya navîn</nowiki> || <nowiki>fransî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>gurciya kevn</nowiki> || <nowiki>gurcî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>karakanî</nowiki> || <nowiki>javayî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>rûmî</nowiki> || <nowiki>javayî</nowiki> || 1 || 0 || [[av/Werger1]] |- | <nowiki>kipçakî</nowiki> || <nowiki>kipchak</nowiki> || 1 || 1 || [[av/Werger1]] |- | <nowiki>îbranî</nowiki> || <nowiki>ladînoyî</nowiki> || 1 || 4 || [[av/Werger1]] |- | <nowiki>latînî</nowiki> || <nowiki>ladînoyî</nowiki> || 1 || 5 || [[av/Werger1]] |- | <nowiki>xcl</nowiki> || <nowiki>ermenî</nowiki> || 1 || 6 || [[Yûnis]] |} oxvyr59srfxzm5eq4olowm8t5stxt3y Bikarhêner:Wikihez/Komên zimanên wergerê 2 1359456 6794084 2026-10-14T21:25:34Z Wikihez 7344 Table of translation nesting groups: en.wiktionary's names and their ku codes and names, for review 6794084 wikitext text/x-wiki Komên zimanan ên ku [[MediaWiki:Gadget-wikt.translation-adder.js|amûra tercimeyan]] rêzên wergerê di bin wan de bi cih dike, li gorî tabloya en.wiktionary ([[:en:MediaWiki:Gadget-TranslationAdder-Data.js]]). Navên kurmancî ji [[Modul:ziman/code to canonical name.json]] û [[Modul:malbat/code to canonical name]] hatine. == Kom (644) == "Çawa": ''ziman'' — koma en.wiktionary ziman e; ''malbat'' — malbatek e ji [[Modul:malbat]]; ''ji rûpelên ku'' — ne di tabloya en.wiktionary de ye, lê rûpelên ku wisa dinivîsin. {| class="wikitable sortable" ! Kod !! Ziman !! en: ziman !! en: kom !! Kod (kom) !! Kom !! Çawa |- | goh || almaniya bilind a kevn || Old High German || German || de || almanî || ziman |- | gmh || almaniya bilind a navîn || Middle High German || German || de || almanî || ziman |- | gmw-ecg || almaniya navendî ya rojhilat || East Central German || German || de || almanî || ziman |- | bar || bayerî || Bavarian || German || de || almanî || ziman |- | gsw || elmanîşî || Alemannic German || German || de || almanî || ziman |- | vmf || frankiya rojhilatî || East Franconian || German || de || almanî || ziman |- | gmw-cfr || frankoniya navendî || Central Franconian || German || de || almanî || ziman |- | gmw-rfr || frankoniya rhine || Rhine Franconian || German || de || almanî || ziman |- | swg || swabî || Swabian || German || de || almanî || ziman |- | apk || apaçiya deştan || Plains Apache || Apache || apa || apaçî || malbat |- | apw || apaçiya rojava || Western Apache || Apache || apa || apaçî || malbat |- | apl || lipan || Lipan || Apache || apa || apaçî || malbat |- | apj || xîkarîyayî || Jicarilla || Apache || apa || apaçî || malbat |- | apm || çîrîkawayî || Chiricahua || Apache || apa || apaçî || malbat |- | aii || aramiya nû ya asûrî || Assyrian Neo-Aramaic || Aramaic || arc || aramî || ziman |- | bhn || aramiya nû ya botanî || Bohtan Neo-Aramaic || Aramaic || arc || aramî || ziman |- | sam || aramiya samarî || Samaritan Aramaic || Aramaic || arc || aramî || ziman |- | hrt || hertevinî || Hértevin || Aramaic || arc || aramî || ziman |- | huy || hulaulayî || Hulaulá || Aramaic || arc || aramî || ziman |- | kqd || koy sanjaq suratî || Koy Sanjaq Surat || Aramaic || arc || aramî || ziman |- | trg || lîşan didanî || Lishán Didán || Aramaic || arc || aramî || ziman |- | lsd || lîşana denî || Lishana Deni || Aramaic || arc || aramî || ziman |- | aij || lîşanîd noşan || Lishanid Noshan || Aramaic || arc || aramî || ziman |- | mid || mandaicî || Mandaic || Aramaic || arc || aramî || ziman |- | myz || mandaîkiya klasîk || Classical Mandaic || Aramaic || arc || aramî || ziman |- | lhs || mlahsö || Mlahsö || Aramaic || arc || aramî || ziman |- | bjf || neo-aramiya cihû ya barzanê || Barzani Jewish Neo-Aramaic || Aramaic || arc || aramî || ziman |- | amw || neo-aramiya rojavayî || Western Neo-Aramaic || Aramaic || arc || aramî || ziman |- | syn || senayayî || Senaya || Aramaic || arc || aramî || ziman |- | syc || siryaniya klasîk || Classical Syriac || Aramaic || arc || aramî || ziman |- | tru || turoyo || Turoyo || Aramaic || arc || aramî || ziman |- | cld || çaldean neo-aramaicî || Chaldean Neo-Aramaic || Aramaic || arc || aramî || ziman |- | inc-mas || asamiya navîn || Middle Assamese || Assamese || as || asamî || ziman |- | inc-oas || asamiya zû || Early Assamese || Assamese || as || asamî || ziman |- | oav || avariya kevn || Old Avar || Avar || av || avarî || ziman |- | inc-oaw || awadhiya kevn || Old Awadhi || Awadhi || awa || awadhî || ziman |- | ahp || aproumu aîzî || Apro || Aizi || kro-aiz || ayizî || malbat |- | ahm || mobumrîn aîzî || Mobu || Aizi || kro-aiz || ayizî || malbat |- | ahi || tîagbamrîn aîzî || Tiagba || Aizi || kro-aiz || ayizî || malbat |- | bqf || baga kaloumî || Baga Kaloum || Baga || alv-bag || baga || malbat |- | bgo || baga kogayî || Baga Koga || Baga || alv-bag || baga || malbat |- | bmd || baga mandurî || Baga Manduri || Baga || alv-bag || baga || malbat |- | bsp || baga sitemuyî || Baga Sitemu || Baga || alv-bag || baga || malbat |- | bsv || baga sobaneyî || Baga Sobané || Baga || alv-bag || baga || malbat |- | akb || angkola batakî || Angkola Batak || Batak || btk || batakî || malbat |- | btz || batakiya alas-kluet || Alas-Kluet Batak || Batak || btk || batakî || malbat |- | btd || batakiya dairî || Dairi Batak || Batak || btk || batakî || malbat |- | btx || batakiya karo || Karo Batak || Batak || btk || batakî || malbat |- | btm || batakiya mandaîlîng || Mandailing Batak || Batak || btk || batakî || malbat |- | bya || batakiya palawan || Palawan Batak || Batak || btk || batakî || malbat |- | bts || batakiya simalungun || Simalungun Batak || Batak || btk || batakî || malbat |- | bbc || bataktobayî || Toba Batak || Batak || btk || batakî || malbat |- | bfc || bayiya bakurî || Panyi Bai || Bai || sit-bai || bayî || malbat |- | bfs || bayiya başûrî || Southern Bai || Bai || sit-bai || bayî || malbat |- | bca || bayiya navendî || Central Bai || Bai || sit-bai || bayî || malbat |- | lay || lama baiyî || Lama Bai || Bai || sit-bai || bayî || malbat |- | inc-obn || bengaliya kevn || Old Bengali || Bengali || bn || bengalî || ziman |- | inc-mbn || bengaliya navîn || Middle Bengali || Bengali || bn || bengalî || ziman |- | kab || kabîlî || Kabyle || — || ber || berberî || ji rûpelên ku |- | rif || tarifitî || Tarifit || — || ber || berberî || ji rûpelên ku |- | shi || taşelhitî || Tashelhit || — || ber || berberî || ji rûpelên ku |- | tzm || temazîxtiya atlasa navendî || Central Atlas Tamazight || — || ber || berberî || ji rûpelên ku |- | shy || şawî || Tachawit || — || ber || berberî || ji rûpelên ku |- | obt || bretoniya kevn || Old Breton || Breton || br || bretonî || ziman |- | xbm || bretoniya navîn || Middle Breton || Breton || br || bretonî || ziman |- | obr || burmayiya kevn || Old Burmese || Burmese || my || burmayî || ziman |- | yln || buyangiya langnî || Langnian Buyang || Buyang || qfa-buy || buyangî || malbat |- | yzg || e'ma buyangî || E'ma Buyang || Buyang || qfa-buy || buyangî || malbat |- | enc || enî || En || Buyang || qfa-buy || buyangî || malbat |- | yrn || yerongî || Yerong || Buyang || qfa-buy || buyangî || malbat |- | bev || daloa beteyî || Daloa Bété || Bété || kro-bet || bétéyî || malbat |- | btg || gagnoa betî || Gagnoa Bété || Bété || kro-bet || bétéyî || malbat |- | bet || gûberowa beteyî || Guiberoua Bété || Bété || kro-bet || bétéyî || malbat |- | gmq-oda || danmarkiya kevn || Old Danish || Danish || da || danmarkî || ziman |- | gie || guebieyî || Guébie || Dida || kro-did || didayî || malbat |- | dic || lakota didayî || Lakota Dida || Dida || kro-did || didayî || malbat |- | gud || yocoboue didayî || Yocoboué Dida || Dida || kro-did || didayî || malbat |- | diw || dinkayiya bakur-rojavayî || Northwestern Dinka || Dinka || din || dinkayî || ziman |- | dip || dinkayiya bakur-rojhilatî || Northeastern Dinka || Dinka || din || dinkayî || ziman |- | dik || dinkayiya başûr-rojavayî || Southwestern Dinka || Dinka || din || dinkayî || ziman |- | dks || dinkayiya başûr-rojhilatî || Southeastern Dinka || Dinka || din || dinkayî || ziman |- | dib || dinkayiya navendî ya başûr || South Central Dinka || Dinka || din || dinkayî || ziman |- | aqd || amparî dogon || Ampari Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dti || ana tinga dogonî || Ana Tinga Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dbw || bankan tey dogonî || Bankan Tey Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dbt || ben teyî || Ben Tey || Dogon || qfa-dgn || dogonî || malbat |- | dbu || bondum dom dogonî || Bondum Dom Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dgb || bunogeyî || Bunoge || Dogon || qfa-dgn || dogonî || malbat |- | dbg || dogul domî || Dogul Dom || Dogon || qfa-dgn || dogonî || malbat |- | dds || donno so dogonî || Donno So Dogon || Dogon || qfa-dgn || dogonî || malbat |- | djm || jamsayî || Jamsay || Dogon || qfa-dgn || dogonî || malbat |- | dmb || mombo dogonî || Mombo Dogon || Dogon || qfa-dgn || dogonî || malbat |- | nzz || nanga dama dogonî || Nanga Dama Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dtu || tebul ure dogonî || Tebul Ure Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dtk || tene kan dogonî || Tene Kan Dogon || Dogon || qfa-dgn || dogonî || malbat |- | tde || tiranige diga dogonî || Tiranige Diga Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dto || tommo soyî || Tommo So || Dogon || qfa-dgn || dogonî || malbat |- | dtm || tomo kan dogonî || Tomo Kan Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dts || toro so dogonî || Toro So Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dtt || toro tegu dogonî || Toro Tegu Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dym || yanda dogonî || Yanda Dogon || Dogon || qfa-dgn || dogonî || malbat |- | cbj || ede cabeyî || Ede Cabe || Ede || alv-ede || ede || malbat |- | ica || ede icayî || Ede Ica || Ede || alv-ede || ede || malbat |- | idd || ede idacayî || Ede Idaca || Ede || alv-ede || ede || malbat |- | ijj || ede ijeyî || Ede Ije || Ede || alv-ede || ede || malbat |- | nqg || ede nagoyî || Ede Nago || Ede || alv-ede || ede || malbat |- | nqk || kura ede nagoyî || Kura Ede Nago || Ede || alv-ede || ede || malbat |- | xkb || manigri-kambole edeyî nagoyî || Manigri-Kambolé Ede Nago || Ede || alv-ede || ede || malbat |- | ife || îfèî || Ifè || Ede || alv-ede || ede || malbat |- | abv || erebiya bahranî || Baharna Arabic || Arabic || ar || erebî || ziman |- | apc || erebiya bakûrê şamê || North Levantine Arabic || Arabic || ar || erebî || ziman |- | ajp || erebiya başûrê şamê || South Levantine Arabic || Arabic || ar || erebî || ziman |- | arq || erebiya cezayirî || Algerian Arabic || Arabic || ar || erebî || ziman |- | xaa || erebiya endulusî || Andalusian Arabic || Arabic || ar || erebî || ziman |- | ary || erebiya fasî || Moroccan Arabic || Arabic || ar || erebî || ziman |- | acw || erebiya hîcazî || Hijazi Arabic || Arabic || ar || erebî || ziman |- | acm || erebiya iraqî || Iraqi Arabic || Arabic || ar || erebî || ziman |- | pga || erebiya jubayê || Juba Arabic || Arabic || ar || erebî || ziman |- | afb || erebiya kendavî || Gulf Arabic || Arabic || ar || erebî || ziman |- | ayl || erebiya lîbyayî || Libyan Arabic || Arabic || ar || erebî || ziman |- | ayp || erebiya mezopotamyayî ya bakur || North Mesopotamian Arabic || Arabic || ar || erebî || ziman |- | arz || erebiya misrî || Egyptian Arabic || Arabic || ar || erebî || ziman |- | ars || erebiya necdî || Najdi Arabic || Arabic || ar || erebî || ziman |- | auz || erebiya ozbekî || Uzbeki Arabic || Arabic || ar || erebî || ziman |- | acy || erebiya qibrisî || Cypriot Arabic || Arabic || ar || erebî || ziman |- | ayn || erebiya sanaanî || Yemeni Arabic || Arabic || ar || erebî || ziman |- | apd || erebiya sudanî || Sudanese Arabic || Arabic || ar || erebî || ziman |- | abh || erebiya tajîkî || Tajiki Arabic || Arabic || ar || erebî || ziman |- | aeb || erebiya tunusî || Tunisian Arabic || Arabic || ar || erebî || ziman |- | acx || erebiya ummanî || Omani Arabic || Arabic || ar || erebî || ziman |- | shu || erebiya çadî || Chadian Arabic || Arabic || ar || erebî || ziman |- | ssh || erebiya şîhhî || Shihhi Arabic || Arabic || ar || erebî || ziman |- | adf || erebîya zufarî || Dhofari Arabic || Arabic || ar || erebî || ziman |- | mey || hassaniyayî || Hassaniya Arabic || Arabic || ar || erebî || ziman |- | xcl || ermeniya kevn || Old Armenian || Armenian || hy || ermenî || ziman |- | axm || ermeniya navîn || Middle Armenian || Armenian || hy || ermenî || ziman |- | fll || faliyiya bakur || North Fali || Fali || fli || faliyî || ziman |- | fal || faliyiya başûr || South Fali || Fali || fli || faliyî || ziman |- | peo || farisiya kevn || Old Persian || Persian || fa || farisî || ziman |- | pal || farisiya navîn || Middle Persian || Persian || fa || farisî || ziman |- | fro || fransiya kevn || Old French || French || fr || fransî || ziman |- | frm || fransiya navîn || Middle French || French || fr || fransî || ziman |- | frr || frîsiya bakur || North Frisian || Frisian || gmw-fri || frîsî || malbat |- | ofs || frîsiya kevn || Old Frisian || Frisian || gmw-fri || frîsî || malbat |- | stq || frîsiya saterlandî || Saterland Frisian || Frisian || gmw-fri || frîsî || malbat |- | fy || frîsî || West Frisian || Frisian || gmw-fri || frîsî || malbat |- | cib || ci gbeyî || Ci Gbe || Gbe || alv-gbe || gbe || malbat |- | gbh || defi gbeyî || Defi Gbe || Gbe || alv-gbe || gbe || malbat |- | gbs || gbesi gbeyî || Gbesi Gbe || Gbe || alv-gbe || gbe || malbat |- | kqk || kotafon gbeyî || Kotafon Gbe || Gbe || alv-gbe || gbe || malbat |- | mxl || maxi gbeyî || Maxi Gbe || Gbe || alv-gbe || gbe || malbat |- | sxw || saxwe gbeyî || Saxwe Gbe || Gbe || alv-gbe || gbe || malbat |- | tfi || tofin gbeyî || Tofin Gbe || Gbe || alv-gbe || gbe || malbat |- | wci || waci gbeyî || Waci Gbe || Gbe || alv-gbe || gbe || malbat |- | wem || weme gbeyî || Weme Gbe || Gbe || alv-gbe || gbe || malbat |- | xwe || xwela gbeyî || Xwela Gbe || Gbe || alv-gbe || gbe || malbat |- | xwl || xwla gbeyiya rojavayî || Western Xwla Gbe || Gbe || alv-gbe || gbe || malbat |- | gbx || xwla gbeyiya rojhilatî || Eastern Xwla Gbe || Gbe || alv-gbe || gbe || malbat |- | aou || a'ou || A'ou || Gelao || gio || gelaoyî || malbat |- | giq || gelaoyiya hêşîn || Green Gelao || Gelao || gio || gelaoyî || malbat |- | gir || gelaoyiya sor || Red Gelao || Gelao || gio || gelaoyî || malbat |- | giw || gelaoyiya spî || White Gelao || Gelao || gio || gelaoyî || malbat |- | giu || mulaoyî || Mulao || Gelao || gio || gelaoyî || malbat |- | gqu || qauyî || Qau || Gelao || gio || gelaoyî || malbat |- | gec || gboloo greboyî || Gboloo Grebo || Grebo || kro-grb || greboyî || malbat |- | gbo || greboyiya bakurî || Northern Grebo || Grebo || kro-grb || greboyî || malbat |- | gry || greboyiya barclayville || Barclayville Grebo || Grebo || kro-grb || greboyî || malbat |- | grj || greboyiya başûrî || Southern Grebo || Grebo || kro-grb || greboyî || malbat |- | grv || greboyiya navendî || Central Grebo || Grebo || kro-grb || greboyî || malbat |- | inc-ogu || gujaratiya kevn || Old Gujarati || Gujarati || gu || gujaratî || ziman |- | inc-mgu || gujaratiya navîn || Middle Gujarati || Gujarati || gu || gujaratî || ziman |- | oge || gurciya kevn || Old Georgian || Georgian || ka || gurcî || ziman |- | ggn || gurungiya rojhilatî || || Gurung || gvr || gurungiya rojavayî || ziman |- | gmq-ogt || gutnîşa kevn || Old Gutnish || Gutnish || gmq-gut || gutnişî || ziman |- | hdn || haidayiya bakurî || Northern Haida || Haida || hai || haidayî || ziman |- | hax || haidayiya başûrî || Southern Haida || Haida || hai || haidayî || ziman |- | inc-ohi || hindiya kevn || Old Hindi || Hindi || hi || hindî || ziman |- | gmw-jdt || danmarkiya jersey || Jersey Dutch || Dutch || nl || holendî || ziman |- | odt || holendiya kevn || Old Dutch || Dutch || nl || holendî || ziman |- | dum || holendiya navîn || Middle Dutch || Dutch || nl || holendî || ziman |- | ojp || japoniya kevn || Old Japanese || Japanese || ja || japonî || ziman |- | jas || javayiya kaledonî ya nû || New Caledonian Javanese || Javanese || jv || javayî || ziman |- | jvn || javayiya karîbî || Caribbean Javanese || Javanese || jv || javayî || ziman |- | kaw || javayiya kevn || Old Javanese || Javanese || jv || javayî || ziman |- | jiy || buyuan jinuoyî || Buyuan Jino || Jino || tbq-jin || jîno || malbat |- | jiu || youle jinuoyî || Youle Jino || Jino || tbq-jin || jîno || malbat |- | kll || kagan kalaganî || Kagan Kalagan || Kalagan || kqe || kalaganî || ziman |- | klg || tagakaulu kalaganî || Tagakaulu Kalagan || Kalagan || kqe || kalaganî || ziman |- | sxk || kalapuyayiya başûrî || Southern Kalapuya || Kalapuya || kyl || kalapuyayî || ziman |- | nrt || kalapuyayî bakurî || Northern Kalapuya || Kalapuya || kyl || kalapuyayî || ziman |- | doc || kamiya bakurî || Northern Kam || Kam || kdx || kamî || ziman |- | kmc || kamiya başûrî || Southern Kam || Kam || kdx || kamî || ziman |- | dra-okn || kannadayiya kevn || Old Kannada || Kannada || kn || kannadayî || ziman |- | dra-mkn || kannadayiya navîn || Middle Kannada || Kannada || kn || kannadayî || ziman |- | kza || karaboroyiya rojavayî || Western Karaboro || Karaboro || alv-krb || karaboro || malbat |- | xrb || karaboroyiya rojhilatî || Eastern Karaboro || Karaboro || alv-krb || karaboro || malbat |- | kvl || brek karenî || Brek Karen || Karen || kar || karenî || malbat |- | bwe || bwe karenî || Bwe Karen || Karen || kar || karenî || malbat |- | kvq || geba karenî || Geba Karen || Karen || kar || karenî || malbat |- | ghk || geko karenî || Geko Karen || Karen || kar || karenî || malbat |- | kvt || lahta karenî || Lahta Karen || Karen || kar || karenî || malbat |- | kxf || manumanaw karenî || Manumanaw Karen || Karen || kar || karenî || malbat |- | blk || pa'o karen || Pa'o Karen || Karen || kar || karenî || malbat |- | jkp || paku karen || Paku Karen || Karen || kar || karenî || malbat |- | ksw || s'gaw karenî || S'gaw Karen || Karen || kar || karenî || malbat |- | kvu || yinbaw karenî || Yinbaw Karen || Karen || kar || karenî || malbat |- | kvy || yintale karenî || Yintale Karen || Karen || kar || karenî || malbat |- | kxk || zayein karenî || Zayein Karen || Karen || kar || karenî || malbat |- | roa-oca || katalaniya kevn || Old Catalan || Catalan || ca || katalanî || ziman |- | bhv || bahauyî || Bahau || Kayan || pdu || kayanî || ziman |- | qwc || keçwayiya klasîk || Classical Quechua || Quechua || qu || keçwayî || ziman |- | kgj || gamale khamî || Gamale Kham || Kham || sit-kha || khamî || malbat |- | kjl || khamî || Western Parbate Kham || Kham || sit-kha || khamî || malbat |- | kif || parbate khamiya rojhilatî || Eastern Parbate Kham || Kham || sit-kha || khamî || malbat |- | kip || şeşi khamî || Sheshi Kham || Kham || sit-kha || khamî || malbat |- | kqs || kissiya bakurî || Northern Kissi || Kissi || alv-kis || kissî || malbat |- | kss || kissiyiya başûrî || Southern Kissi || Kissi || alv-kis || kissî || malbat |- | wni || komoriya ndzwanî || Ndzwani Comorian || Comorian || bnt-com || komorî || malbat |- | swb || komorî || Maore Comorian || Comorian || bnt-com || komorî || malbat |- | wlc || mwalî komorî || Mwali Comorian || Comorian || bnt-com || komorî || malbat |- | zdj || şîngazîcayî || Ngazidja Comorian || Comorian || bnt-com || komorî || malbat |- | kfd || korra koragayî || Korra Koraga || Koraga || dra-kor || koraga || malbat |- | vmd || mudu koragayî || Mudu Koraga || Koraga || dra-kor || koraga || malbat |- | oko || koreyiya kevn || Old Korean || Korean || ko || koreyî || ziman |- | ko-ear || koreyiya modern a pêşî || Early Modern Korean || Korean || ko || koreyî || ziman |- | okm || koreyiya navîn || Middle Korean || Korean || ko || koreyî || ziman |- | oco || korniya kevn || Old Cornish || Cornish || kw || kornî || ziman |- | cnx || korniya navîn || Middle Cornish || Cornish || kw || kornî || ziman |- | gkp || kpelleyiya gîneyî || Guinea Kpelle || Kpelle || kpe || kpelleyî || ziman |- | xpe || liberia kpelleyî || Liberia Kpelle || Kpelle || kpe || kpelleyî || ziman |- | atj || atîkamekwî || Atikamekw || Cree || cr || krî || ziman |- | crk || kriya bejayî || Plains Cree || Cree || cr || krî || ziman |- | crm || kriya mûsî || Moose Cree || Cree || cr || krî || ziman |- | crl || kriya rojhilat ya bakurî || Northern East Cree || Cree || cr || krî || ziman |- | crj || kriya rojhilat ya başûrî || Southern East Cree || Cree || cr || krî || ziman |- | csw || kriya swampî || Swampy Cree || Cree || cr || krî || ziman |- | cwd || kriya woods || Woods Cree || Cree || cr || krî || ziman |- | moe || montagnaîsî || Montagnais || Cree || cr || krî || ziman |- | nsk || naskapî || Naskapi || Cree || cr || krî || ziman |- | kzc || bondoukou kulangoyî || Bondoukou Kulango || Kulango || alv-kul || kulango || malbat |- | nku || bouna kulangoyî || Bouna Kulango || Kulango || alv-kul || kulango || malbat |- | cux || tepeuxila cuicatecî || Tepeuxila Cuicatec || Cuicatec || omq-cui || kuyikatekî || malbat |- | cut || teutila cuicatecî || Teutila Cuicatec || Cuicatec || omq-cui || kuyikatekî || malbat |- | yik || dongşanba laloyî || Dongshanba Lalo || Lalo || tbq-lal || laloyî || malbat |- | ywl || laluyiya rojavayî || Western Lalu || Lalo || tbq-lal || laloyî || malbat |- | yit || laluyiya rojhilatî || Eastern Lalu || Lalo || tbq-lal || laloyî || malbat |- | ywt || xişanba laloyî || Xishanba Lalo || Lalo || tbq-lal || laloyî || malbat |- | roa-ole || leoniya kevn || Old Leonese || Leonese || roa-leo || leonîzî || ziman |- | lia || limbayiya navendî-rojava || West-Central Limba || Limba || alv-lim || lîmba || malbat |- | lma || limbayiya rojhilat || East Limba || Limba || alv-lim || lîmba || malbat |- | olt || lîtwanyayiya kevn || Old Lithuanian || Lithuanian || lt || lîtwanî || ziman |- | bpq || banda malayî || Banda Malay || Malay || ms || malezî || ziman |- | kxd || bruneyî || Brunei Malay || Malay || ms || malezî || ziman |- | jax || jambî malayî || Jambi Malay || Malay || ms || malezî || ziman |- | lrt || larantuka malayî || Larantuka Malay || Malay || ms || malezî || ziman |- | btj || malayiya bakanîsî || Bacanese Malay || Malay || ms || malezî || ziman |- | mhp || malayiya balînîzî || Balinese Malay || Malay || ms || malezî || ziman |- | bve || malayiya berau || Berau Malay || Malay || ms || malezî || ziman |- | bvu || malayiya bukitî || Bukit Malay || Malay || ms || malezî || ziman |- | meo || malayiya kedah || Kedah Malay || Malay || ms || malezî || ziman |- | mqg || malayiya kota bangun kutai || Kota Bangun Kutai Malay || Malay || ms || malezî || ziman |- | mkn || malayiya kupang || Kupang Malay || Malay || ms || malezî || ziman |- | pse || malayiya navendî || Central Malay || Malay || ms || malezî || ziman |- | pmy || malayiya papuayê || Papuan Malay || Malay || ms || malezî || ziman |- | mfa || malayiya pattani || Pattani Malay || Malay || ms || malezî || ziman |- | msi || malayiya sabah || Sabah Malay || Malay || ms || malezî || ziman |- | xmm || manado malayî || Manado Malay || Malay || ms || malezî || ziman |- | zmi || negeri sembilan malayî || Negeri Sembilan Malay || Malay || ms || malezî || ziman |- | poz-sml || sarawak malay || Sarawak Malay || Malay || ms || malezî || ziman |- | vkt || tenggarong kutai malayî || Tenggarong Kutai Malay || Malay || ms || malezî || ziman |- | poz-ter || terengganu malay || Terengganu Malay || Malay || ms || malezî || ziman |- | mku || konyanka maninkayî || Konyanka Maninka || Mandingo || man || mandingoyî || ziman |- | mnk || mandinkayî || Mandinka || Mandingo || man || mandingoyî || ziman |- | msc || sankaran maninkayî || Sankaran Maninka || Mandingo || man || mandingoyî || ziman |- | omp || manipuriya kevn || Old Manipuri || Manipuri || mni || manipurî || ziman |- | bpy || manîpûriya bîşnûpriyayî || Bishnupriya Manipuri || Manipuri || mni || manipurî || ziman |- | msm || agusan manoboyî || Agusan Manobo || Manobo || mno || manoboyî || malbat |- | atd || ata manobo || Ata Manobo || Manobo || mno || manoboyî || malbat |- | mbb || bukidnon manoboyiya rojavayî || Western Bukidnon Manobo || Manobo || mno || manoboyî || malbat |- | mkx || cinamiguin manoboyî || Cinamiguin Manobo || Manobo || mno || manoboyî || malbat |- | mbd || dibabawon manoboyî || Dibabawon Manobo || Manobo || mno || manoboyî || malbat |- | mbi || ilianen manoboyî || Ilianen Manobo || Manobo || mno || manoboyî || malbat |- | mta || kotabato manoboyî || Cotabato Manobo || Manobo || mno || manoboyî || malbat |- | mbt || matigsalug manoboyî || Matigsalug Manobo || Manobo || mno || manoboyî || malbat |- | obo || obo manoboyî || Obo Manobo || Manobo || mno || manoboyî || malbat |- | mqk || rajah kabunsuwan manoboyî || Rajah Kabunsuwan Manobo || Manobo || mno || manoboyî || malbat |- | mbs || saranganî manoboyî || Sarangani Manobo || Manobo || mno || manoboyî || malbat |- | mns-nor || mansiya bakurî || Northern Mansi || Mansi || mns || mansî || malbat |- | mns-sou || mansiya başûrî || Southern Mansi || Mansi || mns || mansî || malbat |- | mns-cen || mansiya merkezî || Central Mansi || Mansi || mns || mansî || malbat |- | omr || marathiya kevn || Old Marathi || Marathi || mr || maratî || ziman |- | daq || dandami mariayî || Dandami Maria || Maria || mds || mariayî || ziman |- | mrr || hill mariayî || Hill Maria || Maria || mds || mariayî || ziman |- | mrj || mariya rojavayî || Western Mari || Mari || chm || marî || malbat |- | mhr || mariya rojhilatî || Eastern Mari || Mari || chm || marî || malbat |- | mzi || ixcatlan mazatecî || Ixcatlán Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | vmy || mazateciya ayautla || Ayautla Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | mau || mazateciya huautla || Huautla Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | maj || mazateciya jalapa de diaz || Jalapa de Díaz Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | maa || mazateciya san jerónimo tecóatl || San Jerónimo Tecóatl Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | maq || mazateciya çiquihuitlan || Chiquihuitlán Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | vmz || mazatekiya mazatlanî || Mazatlán Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | pbm || mazatekiya puebla || Puebla Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | vmp || mazatekiya soyaltepec || Soyaltepec Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | ohu || mecariya kevn || Old Hungarian || Hungarian || hu || mecarî || ziman |- | xme-old || mediya kevn || Old Median || Median || xme || medî || malbat |- | xme-mid || mediya navîn || Middle Median || Median || xme || medî || malbat |- | egx-dem || demotîkî || Demotic Egyptian || Egyptian || egy || misrî || ziman |- | cmg || mongoliya klasîk || Classical Mongolian || Mongolian || mn || mongolî || ziman |- | omx || moniya kevn || Old Mon || Mon || mnw || monî || ziman |- | mkh-mmn || moniya navîn || Middle Mon || Mon || mnw || monî || ziman |- | mnw-tha || thai mon || Thai Mon || Mon || mnw || monî || ziman |- | ymx || mujiya bakurî || Northern Muji || Muji || tbq-muj || mujî || malbat |- | ymc || mujiya başûrî || Southern Muji || Muji || tbq-muj || mujî || malbat |- | ymq || qila mujî || Qila Muji || Muji || tbq-muj || mujî || malbat |- | fmu || muriayiya rojava ya dûr || Far Western Muria || Muria || dra-mur || muriya || malbat |- | mut || muriayiya rojavayî || Western Muria || Muria || dra-mur || muriya || malbat |- | emu || muriayiya rojhilatî || Eastern Muria || Muria || dra-mur || muriya || malbat |- | jmx || juxtlahuaca mixteciya rojavayî || Western Juxtlahuaca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mim || mixteciya alacatlatzala || Alacatlatzala Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mbz || mixteciya amoltepek || Amoltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mip || mixteciya apasco-apoala || Apasco-Apoala Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mib || mixteciya atatlahukayê || Atatláhuca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | miy || mixteciya ayutla || Ayutla Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | miu || mixteciya cacaloxtepec || Cacaloxtepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | miz || mixteciya coatzospan || Coatzospan Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxs || mixteciya huitepec || Huitepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mce || mixteciya itundujia || Itundujia Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxt || mixteciya jamiltepec || Jamiltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxv || mixteciya metlatónoc || Metlatónoc Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mix || mixteciya mixtepec || Mixtepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxy || mixteciya noçixtlana başûr-rojhilatî || Southeastern Nochixtlán Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxa || mixteciya oaxakaya bakur-rojava || Northwest Oaxaca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mie || mixteciya ocotepekî || Ocotepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mil || mixteciya peñoles || Peñoles Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mio || mixteciya pinotepa nacional || Pinotepa Nacional Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mit || mixteciya pueblaya başûr || Southern Puebla Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mjc || mixteciya san juan colorado || San Juan Colorado Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mig || mixteciya san miguel el grande || San Miguel el Grande Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mdv || mixteciya santa lucia monteverde || Santa Lucía Monteverde Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mza || mixteciya santa maria zacatepec || Santa María Zacatepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mks || mixteciya silacayoapan || Silacayoapan Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxb || mixteciya tezoatlan || Tezoatlán Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mtx || mixteciya tidaa || Tidaá Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | meh || mixteciya tlaxiacoya başûr-rojavayî || Southwestern Tlaxiaco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mqh || mixteciya tlazoyaltepek || Tlazoyaltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mtu || mixteciya tututepekî || Tututepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mpm || mixteciya yosondúa || Yosondúa Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mvg || mixteciya yucuañe || Yucuañe Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mab || mixteciya yutanduçî || Yutanduchi Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mih || mixteciya çayuco || Chayuco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mii || mixteciya çigmecatitlan || Chigmecatitlán Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xta || mixtekiya alcozauca || Alcozauca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtu || mixtekiya cuyamecalco || Cuyamecalco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtd || mixtekiya diuxi-tilantongo || Diuxi-Tilantongo Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmj || mixtekiya ixtayutla || Ixtayutla Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmc || mixtekiya juxtlahuaca || Juxtlahuaca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtm || mixtekiya magdalena peñasco || Magdalena Peñasco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmm || mixtekiya mitlatongo || Mitlatongo Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtj || mixtekiya san juan teita || San Juan Teita Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtp || mixtekiya san miguel piedras || San Miguel Piedras Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xts || mixtekiya sindihui || Sindihui Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xti || mixtekiya sinicahua || Sinicahua Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmq || mixtekiya soyaltepec || Soyaltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtt || mixtekiya tacahua || Tacahua Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmx || mixtekiya tamazola || Tamazola Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | omq-tel || mixtekiya teposcolula || Teposcolula Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtl || mixtekiya tijaltepec || Tijaltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtn || mixtekiya tlaxiakoya bakurî || Northern Tlaxiaco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xty || mixtekiya yoloxoçitl || Yoloxochitl Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtb || mixtekiya çazumba || Chazumba Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | azd || durango nahuatliya rojavayî || Eastern Durango Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | azn || durango nahuatliya rojavayî ya jêrîn || Western Durango Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ncj || nahwatliya bakurê puebla || Northern Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhk || nahwatliya cosoleacaque || Cosoleacaque Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ngu || nahwatliya guerrero || Guerrero Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nch || nahwatliya huastecaya navendî || Central Huasteca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhw || nahwatliya huastecaya rojavayî || Western Huasteca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhe || nahwatliya huastekaya rojhilatî || Eastern Huasteca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhq || nahwatliya huaxcaleca || Huaxcaleca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nci || nahwatliya klasîk || Classical Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | naz || nahwatliya koatepek || Coatepec Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhx || nahwatliya mekayapan || Mecayapan Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhm || nahwatliya morelos || Morelos Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ncl || nahwatliya mîçoakanê || Michoacán Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhn || nahwatliya navendî || Central Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhy || nahwatliya oaxakaya bakurî || Northern Oaxaca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nht || nahwatliya ometepec || Ometepec Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nlv || nahwatliya orizaba || Orizaba Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhp || nahwatliya pajapan || Pajapan Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | npl || nahwatliya pueblaya başûr-rojhilatî || Southeastern Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ncx || nahwatliya pueblaya navendî || Central Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhz || nahwatliya santa maria la alta || Santa María La Alta Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nsu || nahwatliya sierra negra || Sierra Negra Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhc || nahwatliya tabasco || Tabasco Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhv || nahwatliya temascaltepec || Temascaltepec Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhg || nahwatliya tetelcingo || Tetelcingo Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nuz || nahwatliya tlamacazapa || Tlamacazapa Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhi || nahwatliya zacatlan-ahuacatlan-tepetzintla || Zacatlán-Ahuacatlán-Tepetzintla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | azz || puebla nahuatliya çiyayî || Highland Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nds-de || almaniya jêrîn a almanî || German Low German || Low German || nds || nedersaksî || ziman |- | gml || almaniya jêrîn a navîn || Middle Low German || Low German || nds || nedersaksî || ziman |- | nds-nl || saksoniya jêrîn ya holendî || Dutch Low Saxon || Low German || nds || nedersaksî || ziman |- | nwc || newariya klasîk || Classical Newar || Newar || new || newarî || ziman |- | nwx || newariya navîn || Middle Newar || Newar || new || newarî || ziman |- | nb || norweciya bokmålî || Norwegian Bokmål || Norwegian/Bokmål || no || norwecî || ziman |- | gmq-mno || norweciya navîn || Middle Norwegian || Norwegian || no || norwecî || ziman |- | nn || norweciya nînorskî || Norwegian Nynorsk || Norwegian/Nynorsk || no || norwecî || ziman |- | nsl || zimanê destan ê norwecî || Norwegian Sign Language || Norwegian || no || norwecî || ziman |- | ngb || ngbandiya bakurî || Northern Ngbandi || Ngbandi || nic-ngd || nîgbandî || malbat |- | nbw || ngbandiya başûrî || Southern Ngbandi || Ngbandi || nic-ngd || nîgbandî || malbat |- | caq || car nicobareseyî || Car Nicobarese || Nicobarese || aav-nic || nîkobarî || malbat |- | ncb || nicobariya navendî || Central Nicobarese || Nicobarese || aav-nic || nîkobarî || malbat |- | nik || nikobariya başûrî || Southern Nicobarese || Nicobarese || aav-nic || nîkobarî || malbat |- | yiv || nisuyiya bakurî || Northern Nisu || Nisu || tbq-nis || nîsu || malbat |- | nsv || nisuyiya başûr-rojavayî || || Nisu || tbq-nis || nîsu || malbat |- | nsd || nisuyiya başûrî || Southern Nisu || Nisu || tbq-nis || nîsu || malbat |- | nos || nisuyiya rojhilatî || Eastern Nisu || Nisu || tbq-nis || nîsu || malbat |- | pro || oksîtaniya kevn || Old Occitan || Occitan || oc || oksîtanî || ziman |- | ort || oriyayiya adivasî || Adivasi Odia || Odia || or || oriyayî || ziman |- | inc-oor || oriyayiya kevn || Old Odia || Odia || or || oriyayî || ziman |- | inc-mor || oriyayiya navîn || Middle Odia || Odia || or || oriyayî || ziman |- | ots || otomiya estado de meksîka || Estado de México Otomi || Otomi || oto-otm || otomî || malbat |- | otm || otomiya highland a rojhilatî || Eastern Highland Otomi || Otomi || oto-otm || otomî || malbat |- | otz || otomiya ixtenco || Ixtenco Otomi || Otomi || oto-otm || otomî || malbat |- | ote || otomiya mezquital || Mezquital Otomi || Otomi || oto-otm || otomî || malbat |- | otq || otomiya queretaro || Querétaro Otomi || Otomi || oto-otm || otomî || malbat |- | ott || otomiya temoaya || Temoaya Otomi || Otomi || oto-otm || otomî || malbat |- | otn || otomiya tenango || Tenango Otomi || Otomi || oto-otm || otomî || malbat |- | otx || otomiya texcatepec || Texcatepec Otomi || Otomi || oto-otm || otomî || malbat |- | otl || otomiya tilapa || Tilapa Otomi || Otomi || oto-otm || otomî || malbat |- | glh || paşayiya bakur-rojavayî || Northwest Pashayi || Pashayi || inc-pas || paşayî || malbat |- | aee || paşayiya bakur-rojhilat || Northeast Pashayi || Pashayi || inc-pas || paşayî || malbat |- | psh || paşayiya başûr-rojava || Southwest Pashayi || Pashayi || inc-pas || paşayî || malbat |- | psi || paşayiya başûr-rojhilat || Southeast Pashayi || Pashayi || inc-pas || paşayî || malbat |- | ypn || ani phowayî || Ani Phowa || Phowa || tbq-phw || phowa || malbat |- | yhl || hlepho phowayî || Hlepho Phowa || Phowa || tbq-phw || phowa || malbat |- | ypb || labo phowayî || Labo Phowa || Phowa || tbq-phw || phowa || malbat |- | zlw-opl || poloniya kevn || Old Polish || Polish || pl || polonî || ziman |- | vkp || kreyoliya portugalî ya korlai || Korlai Creole Portuguese || Portuguese || pt || portugalî || ziman |- | crp-mpp || pidginiya portugalî ya macau || Macau Pidgin Portuguese || Portuguese || pt || portugalî || ziman |- | inc-ash || prakrîtiya aşokanît || Ashokan Prakrit || Prakrit || pra || prakrît || ziman |- | inc-kam || prakrîtiya kamarupî || Kamarupi Prakrit || Prakrit || pra || prakrît || ziman |- | pra-niy || prakrîtiya niya || Niya Prakrit || Prakrit || pra || prakrît || ziman |- | pmu || mirpur panjabî || || Punjabi || pa || puncabî || ziman |- | inc-opa || puncabiya kevn || Old Punjabi || Punjabi || pa || puncabî || ziman |- | rmn || romaniya balkanî || Balkan Romani || Romani || rom || romanî || ziman |- | rml || romaniya baltîkî || Baltic Romani || Romani || rom || romanî || ziman |- | rmf || romaniya kalo fînî || Kalo Finnish Romani || Romani || rom || romanî || ziman |- | rmc || romaniya karpatyayî || Carpathian Romani || Romani || rom || romanî || ziman |- | rmo || romaniya sinte || Sinte Romani || Romani || rom || romanî || ziman |- | rmu || romaniya tavringer || Tavringer Romani || Romani || rom || romanî || ziman |- | rmy || romaniya vlaks || Vlax Romani || Romani || rom || romanî || ziman |- | rmw || romaniya welşî || Welsh Romani || Romani || rom || romanî || ziman |- | slm || pangutaran samayî || Pangutaran Sama || Sama || smd || samayî || ziman |- | ssb || samayiya başûrî || Southern Sama || Sama || smd || samayî || ziman |- | sml || samayiya navendî || Central Sama || Sama || smd || samayî || ziman |- | stj || matya samoyî || Matya Samo || Samo || smq || samoyî || ziman |- | sym || maya samoyî || Maya Samo || Samo || smq || samoyî || ziman |- | sbd || samoyiya başûrî || Southern Samo || Samo || smq || samoyî || ziman |- | sjk || kemi samî || Kemi Sami || Sami || smi || samîkî || malbat |- | sje || pite samî || Pite Sami || Sami || smi || samîkî || malbat |- | sia || samiya akkalayî || Akkala Sami || Sami || smi || samîkî || malbat |- | se || samiya bakurî || Northern Sami || Sami || smi || samîkî || malbat |- | sma || samiya başûr || Southern Sami || Sami || smi || samîkî || malbat |- | sjd || samiya kildînî || Kildin Sami || Sami || smi || samîkî || malbat |- | smj || samiya lule || Lule Sami || Sami || smi || samîkî || malbat |- | sms || samiya skoltî || Skolt Sami || Sami || smi || samîkî || malbat |- | smn || samiya înarî || Inari Sami || Sami || smi || samîkî || malbat |- | sjt || ter samî || Ter Sami || Sami || smi || samîkî || malbat |- | sju || ume samî || Ume Sami || Sami || smi || samîkî || malbat |- | sdn || galûrîsî || Gallurese || — || sc || sardînî || ji rûpelên ku |- | sdc || sassarîsî || Sassarese || — || sc || sardînî || ji rûpelên ku |- | sel-nor || selkupiya bakurî || Northern Selkup || Selkup || sel || selkupî || malbat |- | sel-sou || selkupiya başûrî || Southern Selkup || Selkup || sel || selkupî || malbat |- | gmw-msc || skotlendiya navîn || Middle Scots || Scots || sco || skotî || ziman |- | scs || slaveyiya bakur || North Slavey || Slavey || den || slaveyî || ziman |- | xsl || slaveyiya başûr || South Slavey || Slavey || den || slaveyî || ziman |- | zls-chs || slavonîka dêrê || Church Slavonic || Old Church Slavonic || cu || slaviya kevn || ziman |- | zlw-osk || slovakiya kevn || Old Slovak || Slovak || sk || slovakî || ziman |- | hsb || sorbiya jorîn || Upper Sorbian || Sorbian || wen || sorbî || malbat |- | dsb || sorbiya jêrîn || Lower Sorbian || Sorbian || wen || sorbî || malbat |- | osp || spaniya kevn || Old Spanish || Spanish || es || spanî || ziman |- | osn || sundaniya kevn || Old Sundanese || Sundanese || su || sundanî || ziman |- | gmq-osw || swêdiya kevn || Old Swedish || Swedish || sv || swêdî || ziman |- | oty || tamîliya kevn || Old Tamil || Tamil || ta || tamîlî || ziman |- | pht || phu tayî || Phu Thai || Thai || th || tayî || ziman |- | sou || taiya başûrî || Southern Thai || Thai || th || tayî || ziman |- | nod || tayiya bakurî || Northern Thai || Thai || th || tayî || ziman |- | dra-ote || teluguya kevn || Old Telugu || Telugu || te || telûgûyî || ziman |- | thl || dangaura tharuyî || Dangaura Tharu || Tharu || inc-tha || tharu || malbat |- | tkt || kathoriya tharuyî || Kathoriya Tharu || Tharu || inc-tha || tharu || malbat |- | thq || koçila tharuyî || Kochila Tharu || Tharu || inc-tha || tharu || malbat |- | thr || rana tharuyî || Rana Tharu || Tharu || inc-tha || tharu || malbat |- | the || çitwania tharuyî || Chitwania Tharu || Tharu || inc-tha || tharu || malbat |- | ota || osmanî || Ottoman Turkish || Turkish || tr || tirkî || ziman |- | bgx || tirkiya gagawizî ya balkanan || Balkan Gagauz Turkish || Turkish || tr || tirkî || ziman |- | trk-oat || tirkiya kevn ya anadolî || Old Anatolian Turkish || Turkish || tr || tirkî || ziman |- | kmz || tirkiya xorasanê || Khorasani Turkish || Turkish || tr || tirkî || ziman |- | trc || trîkuyiya copala || Copala Triqui || Triqui || omq-tri || trîkiyî || malbat |- | trq || trîkuyiya san martin itunyoso || San Martín Itunyoso Triqui || Triqui || omq-tri || trîkiyî || malbat |- | trs || trîkuyiya çicahuaxtla || Chicahuaxtla Triqui || Triqui || omq-tri || trîkiyî || malbat |- | tji || tujiayiya bakurî || Northern Tujia || Tujia || sit-tja || tujya || malbat |- | tjs || tujyayiya başûrî || Southern Tujia || Tujia || sit-tja || tujya || malbat |- | xct || tibetiya klasîk || Classical Tibetan || Tibetan || bo || tîbetî || ziman |- | otb || tîbetiya kevn || Old Tibetan || Tibetan || bo || tîbetî || ziman |- | mkh-mvi || viyetnamiya navîn || Middle Vietnamese || Vietnamese || vi || viyetnamî || ziman |- | owl || weylsiya kevn || Old Welsh || Welsh || cy || weylsî || ziman |- | wlm || weylsiya navîn || Middle Welsh || Welsh || cy || weylsî || ziman |- | gxx || weya başûrî || Wè Southern || Wee || kro-wee || wiyî || malbat |- | wec || wè westernî || Wè Western || Wee || kro-wee || wiyî || malbat |- | kca-nor || xantiya bakurî || Northern Khanty || Khanty || kca || xantî || ziman |- | kca-sou || xantiya başûrî || Southern Khanty || Khanty || kca || xantî || ziman |- | kca-eas || xantiya rojhilatî || Eastern Khanty || Khanty || kca || xantî || ziman |- | kxm || ximeriya bakurî || Northern Khmer || Khmer || km || ximêrî || ziman |- | okz || ximeriya kevn || Old Khmer || Khmer || km || ximêrî || ziman |- | xhm || ximeriya navîn || Middle Khmer || Khmer || km || ximêrî || ziman |- | yok-gsy || gaşowu || Gashowu Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-ply || palewyamî || Palewyami Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-bvy || yokutiya buena vîsta || Buena Vista Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-dly || yokutiya deltayê || Delta Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-nvy || yokutiya geliyê bakurî || Northern Valley Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-svy || yokutiya geliyê başûrî || Southern Valley Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-tky || yokutiya tule-kaweah || Tule-Kaweah Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-kry || yokutiya çema kings || Kings River Yokuts || Yokuts || yok || yokutsanî || malbat |- | ykg || yukaghiriya bakurî || Northern Yukaghir || Yukaghir || qfa-yuk || yukaghirî || malbat |- | yux || yukaghiriya başûrî || Southern Yukaghir || Yukaghir || qfa-yuk || yukaghirî || malbat |- | grk-ita || grîkoyî || Italiot Greek || Greek || el || yûnanî || ziman |- | tsd || tsakonî || Tsakonian || Greek || el || yûnanî || ziman |- | yej || yevanîkî || Yevanic || Greek || el || yûnanî || ziman |- | grk-cal || yûnaniya kalabrî || Calabrian Greek || Greek || el || yûnanî || ziman |- | cpg || yûnaniya kapadokyayî || Cappadocian Greek || Greek || el || yûnanî || ziman |- | grc || yûnaniya kevn || Ancient Greek || Greek || el || yûnanî || ziman |- | grk-mar || yûnaniya mariupol || Mariupol Greek || Greek || el || yûnanî || ziman |- | gmy || yûnaniya mîkenî || Mycenaean Greek || Greek || el || yûnanî || ziman |- | pnt || yûnaniya pontî || Pontic Greek || Greek || el || yûnanî || ziman |- | zpd || zapoteciya ixtlana başûr-rojhilatî || Southeastern Ixtlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zsr || zapoteciya rincona başûrî || Southern Rincon Zapotec || Zapotec || zap || zapotekî || ziman |- | zaq || zapotekiya aloapam || Aloápam Zapotec || Zapotec || zap || zapotekî || ziman |- | zpo || zapotekiya amatlan || Amatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zoo || zapotekiya asunción mixtepec || Asunción Mixtepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zaf || zapotekiya ayoquesco || Ayoquesco Zapotec || Zapotec || zap || zapotekî || ziman |- | zad || zapotekiya cajonos || Cajonos Zapotec || Zapotec || zap || zapotekî || ziman |- | zca || zapotekiya coatecas altas || Coatecas Altas Zapotec || Zapotec || zap || zapotekî || ziman |- | zps || zapotekiya coatlan || Coatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zpp || zapotekiya el alto || El Alto Zapotec || Zapotec || zap || zapotekî || ziman |- | zte || zapotekiya elotepec || Elotepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zpg || zapotekiya guevea de humboldt || Guevea de Humboldt Zapotec || Zapotec || zap || zapotekî || ziman |- | xzp || zapotekiya kevnare || Ancient Zapotec || Zapotec || zap || zapotekî || ziman |- | ztl || zapotekiya lapaguia-guivini || Lapaguía-Guivini Zapotec || Zapotec || zap || zapotekî || ziman |- | zpa || zapotekiya laçiguiri || Lachiguiri Zapotec || Zapotec || zap || zapotekî || ziman |- | zpl || zapotekiya laçixio || Lachixío Zapotec || Zapotec || zap || zapotekî || ziman |- | ztp || zapotekiya loxiça || Loxicha Zapotec || Zapotec || zap || zapotekî || ziman |- | zpy || zapotekiya mazaltepec || Mazaltepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zaw || zapotekiya mitla || Mitla Zapotec || Zapotec || zap || zapotekî || ziman |- | zpm || zapotekiya mixtepec || Mixtepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zac || zapotekiya ocotlan || Ocotlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zao || zapotekiya ozolotepec || Ozolotepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zpe || zapotekiya petapa || Petapa Zapotec || Zapotec || zap || zapotekî || ziman |- | zpj || zapotekiya quiavicuzas || Quiavicuzas Zapotec || Zapotec || zap || zapotekî || ziman |- | ztq || zapotekiya quioquitani-quieri || Quioquitani-Quierí Zapotec || Zapotec || zap || zapotekî || ziman |- | zar || zapotekiya rincón || Rincón Zapotec || Zapotec || zap || zapotekî || ziman |- | ztm || zapotekiya san agustin mixtepec || San Agustín Mixtepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zpx || zapotekiya san baltazar loxiça || San Baltazar Loxicha Zapotec || Zapotec || zap || zapotekî || ziman |- | zab || zapotekiya san juan guelavia || San Juan Guelavía Zapotec || Zapotec || zap || zapotekî || ziman |- | ztu || zapotekiya san pablo güila || San Pablo Güilá Zapotec || Zapotec || zap || zapotekî || ziman |- | zpf || zapotekiya san pedro quiatoni || San Pedro Quiatoni Zapotec || Zapotec || zap || zapotekî || ziman |- | zpt || zapotekiya san vicente coatlan || San Vicente Coatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | ztn || zapotekiya santa catarina albarradas || Santa Catarina Albarradas Zapotec || Zapotec || zap || zapotekî || ziman |- | zpn || zapotekiya santa ines yatzeçi || Santa Inés Yatzechi Zapotec || Zapotec || zap || zapotekî || ziman |- | zpi || zapotekiya santa maria quiegolani || Santa María Quiegolani Zapotec || Zapotec || zap || zapotekî || ziman |- | zpr || zapotekiya santiago xanica || Santiago Xanica Zapotec || Zapotec || zap || zapotekî || ziman |- | zas || zapotekiya santo domingo albarradas || Santo Domingo Albarradas Zapotec || Zapotec || zap || zapotekî || ziman |- | zaa || zapotekiya sierra de juarez || Sierra de Juárez Zapotec || Zapotec || zap || zapotekî || ziman |- | zat || zapotekiya tabaa || Tabaa Zapotec || Zapotec || zap || zapotekî || ziman |- | ztt || zapotekiya tejalapan || Tejalapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zpz || zapotekiya texmelucan || Texmelucan Zapotec || Zapotec || zap || zapotekî || ziman |- | zts || zapotekiya tilquiapan || Tilquiapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zpk || zapotekiya tlacolulita || Tlacolulita Zapotec || Zapotec || zap || zapotekî || ziman |- | zph || zapotekiya totomaçapan || Totomachapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zax || zapotekiya xadani || Xadani Zapotec || Zapotec || zap || zapotekî || ziman |- | ztg || zapotekiya xanaguia || Xanaguía Zapotec || Zapotec || zap || zapotekî || ziman |- | zpu || zapotekiya yalalagî || Yalálag Zapotec || Zapotec || zap || zapotekî || ziman |- | zae || zapotekiya yareni || Yareni Zapotec || Zapotec || zap || zapotekî || ziman |- | zty || zapotekiya yatee || Yatee Zapotec || Zapotec || zap || zapotekî || ziman |- | zav || zapotekiya yatzaçî || Yatzachi Zapotec || Zapotec || zap || zapotekî || ziman |- | zpb || zapotekiya yautepec || Yautepec Zapotec || Zapotec || zap || zapotekî || ziman |- | ztx || zapotekiya zaaçila || Zaachila Zapotec || Zapotec || zap || zapotekî || ziman |- | zpw || zapotekiya zaniza || Zaniza Zapotec || Zapotec || zap || zapotekî || ziman |- | zpq || zapotekiya zoogoço || Zoogocho Zapotec || Zapotec || zap || zapotekî || ziman |- | zpv || zapotekiya çiçicapan || Chichicapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zpc || zapotekiya çoapan || Choapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zam || zapotekoyiya mahuatlana navendî || Central Mahuatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zai || îsthmus zapotekî || Isthmus Zapotec || Zapotec || zap || zapotekî || ziman |- | cya || nopala çatinoyî || Nopala Chatino || Chatino || omq-cha || çatînoyî || malbat |- | omq-sjq || san Juan quiahije chatino || San Juan Quiahije Chatino || Chatino || omq-cha || çatînoyî || malbat |- | cta || tataltepec çatinoyî || Tataltepec Chatino || Chatino || omq-cha || çatînoyî || malbat |- | omq-teo || teojomulco çatinoyî || Teojomulco Chatino || Chatino || omq-cha || çatînoyî || malbat |- | ctz || zacatepec çatinoyî || Zacatepec Chatino || Chatino || omq-cha || çatînoyî || malbat |- | czn || zenzontepekiya çatîno || Zenzontepec Chatino || Chatino || omq-cha || çatînoyî || malbat |- | cly || çatinoyiya eastern highland || Eastern Highland Chatino || Chatino || omq-cha || çatînoyî || malbat |- | ctp || çatinoyiya western highland || Western Highland Chatino || Chatino || omq-cha || çatînoyî || malbat |- | zlw-ocs || çekiya kevn || Old Czech || Czech || cs || çekî || ziman |- | nan-dat || datian min || Datian Min || Chinese || zh || çînî || ziman |- | dng || dunganî || Dungan || Chinese || zh || çînî || ziman |- | bpn || dzao minî || Dzao Min || Chinese || zh || çînî || ziman |- | gan || ganî || Gan || Chinese || zh || çînî || ziman |- | nan-hlh || hailufeng min || Hailufeng Min || Chinese || zh || çînî || ziman |- | hak || hakkayî || Hakka || Chinese || zh || çînî || ziman |- | hnm || haynanîzî || Hainanese || Chinese || zh || çînî || ziman |- | nan-hbl || hokkien || Hokkien || Chinese || zh || çînî || ziman |- | czh || huizhouyî || Huizhou || Chinese || zh || çînî || ziman |- | cjy || jinî || Jin || Chinese || zh || çînî || ziman |- | yue || kantonî || Cantonese || Chinese || zh || çînî || ziman |- | mis-hkl || kelantan peranakan hokkien || Kelantan Peranakan Hokkien || Chinese || zh || çînî || ziman |- | luh || leizhou min || Leizhou Min || Chinese || zh || çînî || ziman |- | nan-lnx || longyan min || Longyan Min || Chinese || zh || çînî || ziman |- | cmn || mandarînî || Mandarin || Chinese || zh || çînî || ziman |- | mnp || min beî || Northern Min || Chinese || zh || çînî || ziman |- | cdo || min dongî || Eastern Min || Chinese || zh || çînî || ziman |- | czo || min zhongî || Central Min || Chinese || zh || çînî || ziman |- | nan || minnanî || Min Nan || Chinese || zh || çînî || ziman |- | csp || pinghuaya başûrî || Southern Pinghua || Chinese || zh || çînî || ziman |- | cpx || puxianî || Puxian Min || Chinese || zh || çînî || ziman |- | cnp || pînghuaya bakurî || Northern Pinghua || Chinese || zh || çînî || ziman |- | nan-zsh || sanxiang min || Sanxiang Min || Chinese || zh || çînî || ziman |- | sjc || shaojiang min || Shaojiang Min || Chinese || zh || çînî || ziman |- | zhx-sic || siçuanîzî || Sichuanese || Chinese || zh || çînî || ziman |- | zhx-tai || taişanîzî || Taishanese || Chinese || zh || çînî || ziman |- | mis-tnw || tangwang || Tangwang || Chinese || zh || çînî || ziman |- | nan-tws || teoçewî || Teochew || Chinese || zh || çînî || ziman |- | wxa || waxianghuayî || Waxiang || Chinese || zh || çînî || ziman |- | wuu || wuyî || Wu || Chinese || zh || çînî || ziman |- | hsn || xiangî || Xiang || Chinese || zh || çînî || ziman |- | nan-zhe || zhenan min || Zhenan Min || Chinese || zh || çînî || ziman |- | och || çîniya kevn || Old Chinese || Chinese || zh || çînî || ziman |- | lzh || çîniya klasîk || Classical Chinese || Chinese || zh || çînî || ziman |- | ltc || çîniya navîn || Middle Chinese || Chinese || zh || çînî || ziman |- | zhx-sht || şaozhou tuhuayî || Shaozhou Tuhua || Chinese || zh || çînî || ziman |- | smp || îbraniya samarî || Samaritan Hebrew || Hebrew || he || îbranî || ziman |- | ike || înuktitutiya kanadayî ya rojhilatî || Eastern Canadian Inuktitut || Inuktitut || iu || înuîtî || ziman |- | sga || îrlendiya kevn || Old Irish || Irish || ga || îrlendî || ziman |- | mga || îrlendiya navîn || Middle Irish || Irish || ga || îrlendî || ziman |- | pgl || îrlendiya prîmîtîv || Primitive Irish || Irish || ga || îrlendî || ziman |} == Nehatine wergerandin (496) == ''kom bê kod'' — koma en.wiktionary ne ziman e ne jî malbat; ''kod li ku tune'' — en.wiktionary kodek dide, lê li ku ne ziman e ne jî malbat; ''ziman li ku tune'' — ziman bi xwe li ku tune ye; ''heman ziman'' — en.wiktionary rêza zimanî di bin heman zimanî de bi alfabeyeke din dinivîse (wek Serbo-Croatian/Cyrillic). {| class="wikitable sortable" ! Kod !! Ziman !! en: ziman !! en: kom !! en: kod (kom) !! Çima |- | gn-cls || guwaraniya klasîk || Classical Guarani || Guarani || gn || ? |- | gnw || guaraniyiya bolîvyaya rojavayî || Western Bolivian Guarani || Guarani || gn || ? |- | gug || guaraniyiya paraguayanî || Paraguayan Guarani || Guarani || gn || ? |- | gui || guaraniyiya bolivyayî ya rojhilatî || Eastern Bolivian Guarani || Guarani || gn || ? |- | gun || mbya guaraniyî || Mbya Guarani || Guarani || gn || ? |- | ckb || soranî || Central Kurdish || Kurdish || ku || ? |- | lki || lekî || Laki || Kurdish || ku || ? |- | sdh || kurdiya başûrî || Southern Kurdish || Kurdish || ku || ? |- | syd-fne || nenetsiya daristanî || || Nenets || yrk || ? |- | gvr || gurungiya rojavayî || Gurung || Gurung || gvr || heman ziman |- | man || mandingoyî || Mandingo || Mandingo || man || heman ziman |- | yrk || nenetsiya tundra || Nenets || Nenets || yrk || heman ziman |- | koi || komî-permyakî || Komi-Permyak || Komi || kv || kod li ku tune (kv) |- | kpv || komî-zîriyî || Komi-Zyrian || Komi || kv || kod li ku tune (kv) |- | urj-koo || komiya kevn || Old Komi || Komi || kv || kod li ku tune (kv) |- | urj-kya || komî-yazva || Komi-Yazva || Komi || kv || kod li ku tune (kv) |- | asc || asmatiya casuarina coast || Casuarina Coast Asmat || Asmat || ngf-asm || kod li ku tune (ngf-asm) |- | asy || yaosakor asmat || Yaosakor Asmat || Asmat || ngf-asm || kod li ku tune (ngf-asm) |- | cns || asmatiya navendî || Central Asmat || Asmat || ngf-asm || kod li ku tune (ngf-asm) |- | nks || asmatiya bakur || North Asmat || Asmat || ngf-asm || kod li ku tune (ngf-asm) |- | aws || awyuya başûrî || South Awyu || Awyu || ngf-awy || kod li ku tune (ngf-awy) |- | awu || awyuya navendî || Central Awyu || Awyu || ngf-awy || kod li ku tune (ngf-awy) |- | awv || awyuya jair || Jair Awyu || Awyu || ngf-awy || kod li ku tune (ngf-awy) |- | awy || awyuya edura || Edera Awyu || Awyu || ngf-awy || kod li ku tune (ngf-awy) |- | psa || asue awyuyî || Asue Awyu || Awyu || ngf-awy || kod li ku tune (ngf-awy) |- | yir || awyuyiya bakur || North Awyu || Awyu || ngf-awy || kod li ku tune (ngf-awy) |- | dna || daniya geliya grand a jorîn || Upper Grand Valley Dani || Dani || ngf-dan || kod li ku tune (ngf-dan) |- | dni || daniya geliya grand a jêrîn || Lower Grand Valley Dani || Dani || ngf-dan || kod li ku tune (ngf-dan) |- | dnt || daniya geliya grand a naverast || Mid Grand Valley Dani || Dani || ngf-dan || kod li ku tune (ngf-dan) |- | dnw || daniya rojavayî || Western Dani || Dani || ngf-dan || kod li ku tune (ngf-dan) |- | omw || tairorayiya başûr || South Tairora || Tairora || ngf-tai || kod li ku tune (ngf-tai) |- | tbg || tayrorayiya bakur || North Tairora || Tairora || ngf-tai || kod li ku tune (ngf-tai) |- | nlk || ninia yalî || Ninia Yali || Yali || ngf-yal || kod li ku tune (ngf-yal) |- | yac || pass valley yalî || Pass Valley Yali || Yali || ngf-yal || kod li ku tune (ngf-yal) |- | yli || angguruk yalî || Angguruk Yali || Yali || ngf-yal || kod li ku tune (ngf-yal) |- | enf || forest enetsî || Forest Enets || Enets || syd-ene || kod li ku tune (syd-ene) |- | enh || tundra enetsî || Tundra Enets || Enets || syd-ene || kod li ku tune (syd-ene) |- | abd || manîdeyî || Camarines Norte Agta || Agta || || kom bê kod |- | agk || îsarog agtayî || Isarog Agta || Agta || || kom bê kod |- | agt || agtayiya kagayanî ya navendî || Central Cagayan Agta || Agta || || kom bê kod |- | agv || remontado agta || Remontado Agta || Agta || || kom bê kod |- | agz || mount îrîga agta || Mount Iriga Agta || Agta || || kom bê kod |- | apf || pahanan agta || Pahanan Agta || Agta || || kom bê kod |- | atl || agtaya çiyaya iraya || Mount Iraya Agta || Agta || || kom bê kod |- | dgc || casiguran dumagat agtayî || Casiguran Dumagat Agta || Agta || || kom bê kod |- | due || umiray dumaget agtayî || Umiray Dumaget Agta || Agta || || kom bê kod |- | dul || alabat island agtayî || Alabat Island Agta || Agta || || kom bê kod |- | duo || dupaningan agtayî || Dupaningan Agta || Agta || || kom bê kod |- | duy || dicamay agtayî || Dicamay Agta || Agta || || kom bê kod |- | dyg || villa viciosa agtayî || || Agta || || kom bê kod |- | phi-din || dinapigue agtayî || Dinapigue Agta || Agta || || kom bê kod |- | phi-nag || nagtipunan agtayî || Nagtipunan Agta || Agta || || kom bê kod |- | agy || altaya başûrî || Southern Alta || Alta || || kom bê kod |- | aqn || altaya bakurî || Northern Alta || Alta || || kom bê kod |- | alt || altayiya başûrî || Southern Altai || Altai || || kom bê kod |- | atv || altayiya bakurî || Northern Altai || Altai || || kom bê kod |- | ams || amamî-oşîmayiya başûrî || Southern Amami Ōshima || Amami Ōshima || || kom bê kod |- | ryn || amamî-oşimayiya bakurî || Northern Amami Ōshima || Amami Ōshima || || kom bê kod |- | amu || amuzgoyî || Guerrero Amuzgo || Amuzgo || || kom bê kod |- | azg || san pedro amuzgos amuzgo || San Pedro Amuzgos Amuzgo || Amuzgo || || kom bê kod |- | azm || amuzgoyiya îpalapayî || Ipalapa Amuzgo || Amuzgo || || kom bê kod |- | aer || arrernteyiya rojavayî || Eastern Arrernte || Arrernte || || kom bê kod |- | are || arrernteya rojavayî || Western Arrernte || Arrernte || || kom bê kod |- | cjo || aşeninka pajonalî || Pajonal Ashéninka || Ashéninka || || kom bê kod |- | cpb || ucayali-yurúa aşeninkayî || Ucayali-Yurúa Ashéninka || Ashéninka || || kom bê kod |- | cpc || ajyininka apurucayaliyî || Apurucayali Ashéninka || Ashéninka || || kom bê kod |- | cpu || piçis aşeninkayî || Pichis Ashéninka || Ashéninka || || kom bê kod |- | cpy || south ucayali aşeninkayî || South Ucayali Ashéninka || Ashéninka || || kom bê kod |- | prq || aşeninka pereneyî || Perené Ashéninka || Ashéninka || || kom bê kod |- | atp || pudtol atta || Pudtol Atta || Atta || || kom bê kod |- | att || pamplona atta || Pamplona Atta || Atta || || kom bê kod |- | azt || faîre atta || Faire Atta || Atta || || kom bê kod |- | abc || ambala ayta || Ambala Ayta || Ayta || || kom bê kod |- | abp || abenlen ayta || Abenlen Ayta || Ayta || || kom bê kod |- | ays || sorsogon aytayî || Sorsogon Ayta || Ayta || || kom bê kod |- | ayt || bataan aytayî || Bataan Ayta || Ayta || || kom bê kod |- | ayy || tayabas aytayî || || Ayta || || kom bê kod |- | blx || mag-îndî ayta || Mag-Indi Ayta || Ayta || || kom bê kod |- | sgb || mag-ançî aytayî || Mag-Anchi Ayta || Ayta || || kom bê kod |- | bdl || bajwayiya endonezyayî || Indonesian Bajau || Bajau || || kom bê kod |- | bdr || bajwayiya kendava rojava || West Coast Bajau || Bajau || || kom bê kod |- | bfb || paurî barelî || Pauri Bareli || Bareli || || kom bê kod |- | bgd || rathwî barelî || Rathwi Bareli || Bareli || || kom bê kod |- | bpx || palya bareliyî || Palya Bareli || Bareli || || kom bê kod |- | nes || bhoti kinnaurî || Bhoti Kinnauri || Bhoti || || kom bê kod |- | sbu || stod bhotî || Stod Bhoti || Bhoti || || kom bê kod |- | spt || spîtî bhotî || Spiti Bhoti || Bhoti || || kom bê kod |- | bth || biatah bidayuhî || Biatah Bidayuh || Bidayuh || || kom bê kod |- | sdo || bukar-sadung bidayuhî || Bukar-Sadung Bidayuh || Bidayuh || || kom bê kod |- | sne || bau bidayuh || Bau Bidayuh || Bidayuh || || kom bê kod |- | bcl || bîkoliya naverast || Central Bikol || Bikol || || kom bê kod |- | bln || bîkolanoyiya başûr katanduanî || Southern Catanduanes Bikol || Bikol || || kom bê kod |- | bto || iriga bikolanoyî || Rinconada Bikol || Bikol || || kom bê kod |- | cts || bicolanoyiya northern catanduanes || Northern Catanduanes Bikol || Bikol || || kom bê kod |- | fbl || west albay bikolî || West Miraya Bikol || Bikol || || kom bê kod |- | lbl || libon bikolî || Libon Bikol || Bikol || || kom bê kod |- | rbl || miraya bikolî || East Miraya Bikol || Bikol || || kom bê kod |- | ubl || buhi'non bikolî || Buhi'non Bikol || Bikol || || kom bê kod |- | bfo || malba bîrîforî || Malba Birifor || Birifor || || kom bê kod |- | biv || biriforiya başûr || Southern Birifor || Birifor || || kom bê kod |- | bpr || koronadal blaanî || Koronadal Blaan || Blaan || || kom bê kod |- | bps || saranganî blaanî || Sarangani Blaan || Blaan || || kom bê kod |- | bnt-sbo || bomaya başûr || South Boma || Boma || || kom bê kod |- | boh || bomayî || North Boma || Boma || || kom bê kod |- | ebk || bontociya rojhilatî || Eastern Bontoc || Bontoc || || kom bê kod |- | lbk || bontociya navendî || Central Bontoc || Bontoc || || kom bê kod |- | obk || bontociya başûrî || Southern Bontoc || Bontoc || || kom bê kod |- | rbk || bontokiya bakurî || Northern Bontoc || Bontoc || || kom bê kod |- | vbk || bontokiya başûr-rojavayî || Southwestern Bontoc || Bontoc || || kom bê kod |- | boo || bozoyoya tiemacewe || Tiemacèwè Bozo || Bozo || || kom bê kod |- | boz || bozoyiya tieyaxo || Tiéyaxo Bozo || Bozo || || kom bê kod |- | bze || jenaama bozoyî || Jenaama Bozo || Bozo || || kom bê kod |- | bzx || bozoyiya hainyaxo || Hainyaxo Bozo || Bozo || || kom bê kod |- | bru || bruyiya rojhilatî || Eastern Bru || Bru || || kom bê kod |- | brv || bruyiya rojavayî || Western Bru || Bru || || kom bê kod |- | bzh || mapos buangî || Mapos Buang || Buang || || kom bê kod |- | mmo || mangga buangî || Mangga Buang || Buang || || kom bê kod |- | buh || younuo bunuyî || Younuo Bunu || Bunu || || kom bê kod |- | bwn || wunay bunuyî || Wunai Bunu || Bunu || || kom bê kod |- | bwx || bu-nao bunuyî || Bu-Nao Bunu || Bunu || || kom bê kod |- | pnu || jiongnai bunuyî || Jiongnai Bunu || Bunu || || kom bê kod |- | cja || çamiya rojavayî || Western Cham || Cham || || kom bê kod |- | cjm || çamiya rojhilatî || Eastern Cham || Cham || || kom bê kod |- | bgr || bawm çînî || Bawm Chin || Chin || || kom bê kod |- | cbl || bualkhaw çinî || Bualkhaw Chin || Chin || || kom bê kod |- | cfm || falam çinî || Falam Chin || Chin || || kom bê kod |- | ckn || kaang çinî || Kaang Chin || Chin || || kom bê kod |- | clj || laitu çinî || Laitu Chin || Chin || || kom bê kod |- | cmr || mro çinî || Mro Chin || Chin || || kom bê kod |- | cnb || çinbon çinî || Chinbon Chin || Chin || || kom bê kod |- | cnk || khumi çinî || Khumi Chin || Chin || || kom bê kod |- | cnw || ngawn çinî || Ngawn Chin || Chin || || kom bê kod |- | csh || aşo çinî || Asho Chin || Chin || || kom bê kod |- | csj || songlai çinî || Songlai Chin || Chin || || kom bê kod |- | csv || sumtu çinî || Sumtu Chin || Chin || || kom bê kod |- | csy || siyin çinî || Siyin Chin || Chin || || kom bê kod |- | ctd || tedim çinî || Tedim Chin || Chin || || kom bê kod |- | cth || thaiphum çinî || Thaiphum Chin || Chin || || kom bê kod |- | czt || zotung çinî || Zotung Chin || Chin || || kom bê kod |- | dao || daai çinî || Daai Chin || Chin || || kom bê kod |- | mrh || mara çinî || Mara Chin || Chin || || kom bê kod |- | mwq || mün çinî || Mün Chin || Chin || || kom bê kod |- | sez || senthang çinî || Senthang Chin || Chin || || kom bê kod |- | tcp || tawr çinî || Tawr Chin || Chin || || kom bê kod |- | tcz || thado çinî || Thado Chin || Chin || || kom bê kod |- | cco || comaltepec çinantecî || Comaltepec Chinantec || Chinantec || || kom bê kod |- | chj || ojitlan çinantecî || Ojitlán Chinantec || Chinantec || || kom bê kod |- | chq || quiotepec çinantecî || Quiotepec Chinantec || Chinantec || || kom bê kod |- | chz || ozumacin çinantecî || Ozumacín Chinantec || Chinantec || || kom bê kod |- | cle || lealao çinantecî || Lealao Chinantec || Chinantec || || kom bê kod |- | cnl || lalana çinantecî || Lalana Chinantec || Chinantec || || kom bê kod |- | cnt || tepetotutla çinantecî || Tepetotutla Chinantec || Chinantec || || kom bê kod |- | cpa || palantla çinantecî || Palantla Chinantec || Chinantec || || kom bê kod |- | csa || çiltepec çinantecî || Chiltepec Chinantec || Chinantec || || kom bê kod |- | cso || soçiapam çinantecî || Sochiapam Chinantec || Chinantec || || kom bê kod |- | cte || tepinapa çinantecî || Tepinapa Chinantec || Chinantec || || kom bê kod |- | ctl || tlacoatzintepec çinantecî || Tlacoatzintepec Chinantec || Chinantec || || kom bê kod |- | cuc || usila çinantecî || Usila Chinantec || Chinantec || || kom bê kod |- | cvn || valle nacional çinantecî || Valle Nacional Chinantec || Chinantec || || kom bê kod |- | chd || highland oaxaca çontalî || Highland Oaxaca Chontal || Chontal || || kom bê kod |- | clo || lowland oaxaca çontalî || Lowland Oaxaca Chontal || Chontal || || kom bê kod |- | crq || iyo'wujwa çoroteyî || Iyo'wujwa Chorote || Chorote || || kom bê kod |- | crt || iyojwa'ja çoroteyî || Iyojwa'ja Chorote || Chorote || || kom bê kod |- | dtp || dusuniya navendî || Central Dusun || Dusun || || kom bê kod |- | kzs || sugut dusunî || Sugut Dusun || Dusun || || kom bê kod |- | kzt || tambunan dusunî || || Dusun || || kom bê kod |- | tdu || tempasuk dusunî || || Dusun || || kom bê kod |- | erk || efateyiya başûr || South Efate || Efate || || kom bê kod |- | llp || efateyiya bakur || North Efate || Efate || || kom bê kod |- | gas || adiwasi garasiayî || Adiwasi Garasia || Garasia || || kom bê kod |- | gra || rajput garasiayî || Rajput Garasia || Garasia || || kom bê kod |- | ghe || ghaleyiya başûrî || Southern Ghale || Ghale || || kom bê kod |- | ghh || ghaleyî bakurî || Northern Ghale || Ghale || || kom bê kod |- | ght || kutang ghaleyî || Kutang Ghale || Ghale || || kom bê kod |- | gis || gizigayiya bakur || North Giziga || Giziga || || kom bê kod |- | giz || gizigayiya başûr || South Giziga || Giziga || || kom bê kod |- | hnd || hindkoyiya başûrî || Southern Hindko || Hindko || || kom bê kod |- | hno || hindkoyiya bakurî || Northern Hindko || Hindko || || kom bê kod |- | cqd || çuanqiandian cluster miaoyî || Chuanqiandian Cluster Miao || Hmong || || kom bê kod |- | hea || qiandong miaoyiya bakurî || Northern Qiandong Miao || Hmong || || kom bê kod |- | hma || maşan hmongiya başûrî || Southern Mashan Hmong || Hmong || || kom bê kod |- | hmc || huişui hmongiya navendî || Central Huishui Hmong || Hmong || || kom bê kod |- | hmd || a-hmaoyî || A-Hmao || Hmong || || kom bê kod |- | hme || huişui hmongiya rojhilatî || Eastern Huishui Hmong || Hmong || || kom bê kod |- | hmf || hmong donî || Hmong Don || Hmong || || kom bê kod |- | hmg || guiyang hmongiya başûr-rojavayî || Southwestern Guiyang Hmong || Hmong || || kom bê kod |- | hmh || huişui hmongiya başûr-rojavayî || Southwestern Huishui Hmong || Hmong || || kom bê kod |- | hmi || huişui hmongiya bakurî || Northern Huishui Hmong || Hmong || || kom bê kod |- | hmj || geyî || Ge || Hmong || || kom bê kod |- | hml || luopohe hmongî || Luopohe Hmong || Hmong || || kom bê kod |- | hmm || maşan hmongiya navendî || Central Mashan Hmong || Hmong || || kom bê kod |- | hmp || maşan hmongiya bakurî || Northern Mashan Hmong || Hmong || || kom bê kod |- | hmq || qiandong miaoyiya rojhilatî || Eastern Qiandong Miao || Hmong || || kom bê kod |- | hms || qiandong miaoyiya başûrî || Southern Qiandong Miao || Hmong || || kom bê kod |- | hmv || hmong dôî || Hmong Dô || Hmong || || kom bê kod |- | hmw || maşan hmongiya rojavayî || Western Mashan Hmong || Hmong || || kom bê kod |- | hmy || guiyang hmongiya başûrî || Southern Guiyang Hmong || Hmong || || kom bê kod |- | hmz || hmong şuayî || Hmong Shua || Hmong || || kom bê kod |- | hnj || hmongiya hêşîn || Green Hmong || Hmong || || kom bê kod |- | hrm || horned miaoyî || Horned Miao || Hmong || || kom bê kod |- | huj || guiyang hmongiya bakurî || Northern Guiyang Hmong || Hmong || || kom bê kod |- | mmr || xiangxi miaoyiya rojavayî || Western Xiangxi Miao || Hmong || || kom bê kod |- | muq || xiangxi miaoyiya rojhilatî || Eastern Xiangxi Miao || Hmong || || kom bê kod |- | mww || hmongî || White Hmong || Hmong || || kom bê kod |- | sfm || small flowery miaoyî || Small Flowery Miao || Hmong || || kom bê kod |- | hto || minica huitotoyî || Minica Huitoto || Huitoto || || kom bê kod |- | huu || murui huitotoyî || Murui Huitoto || Huitoto || || kom bê kod |- | hux || nüpode huitotoyî || Nüpode Huitoto || Huitoto || || kom bê kod |- | ifa || amganad ifugaoyî || Amganad Ifugao || Ifugao || || kom bê kod |- | ifb || batad ifugaoyî || Batad Ifugao || Ifugao || || kom bê kod |- | ifk || tuwali ifugaoyî || Tuwali Ifugao || Ifugao || || kom bê kod |- | ifu || mayoyao ifugaoyî || Mayoyao Ifugao || Ifugao || || kom bê kod |- | bjx || banao îtneg || Banao Itneg || Itneg || || kom bê kod |- | itb || binongan itnegî || Binongan Itneg || Itneg || || kom bê kod |- | iti || înlaod itnegî || Inlaod Itneg || Itneg || || kom bê kod |- | itt || maeng itnegî || Maeng Itneg || Itneg || || kom bê kod |- | ity || moyadan itnegî || Moyadan Itneg || Itneg || || kom bê kod |- | tis || masadiit itnegî || Masadiit Itneg || Itneg || || kom bê kod |- | dtb || labuk-kinabatangan kadazanî || Labuk-Kinabatangan Kadazan || Kadazan || || kom bê kod |- | kqt || kadazaniya çemê kliasê || Klias River Kadazan || Kadazan || || kom bê kod |- | kzj || kadazaniya beravî || Coastal Kadazan || Kadazan || || kom bê kod |- | kzf || da'a kailiyî || Da'a Kaili || Kaili || || kom bê kod |- | lew || ledo kailiyî || Ledo Kaili || Kaili || || kom bê kod |- | unz || unde kailî || Unde Kaili || Kaili || || kom bê kod |- | kkg || mabaka valley kalingayî || Mabaka Valley Kalinga || Kalinga || || kom bê kod |- | kmd || madukayang kalingayî || Madukayang Kalinga || Kalinga || || kom bê kod |- | kmk || limos kalingayî || Limos Kalinga || Kalinga || || kom bê kod |- | kml || tanudan kalingayî || Tanudan Kalinga || Kalinga || || kom bê kod |- | knb || lubuagan kalingayî || Lubuagan Kalinga || Kalinga || || kom bê kod |- | ksc || kalingayiya başûrî || Southern Kalinga || Kalinga || || kom bê kod |- | kyb || butbut kalingayî || Butbut Kalinga || Kalinga || || kom bê kod |- | ify || keley-i kallahanî || Keley-I Kallahan || Kallahan || || kom bê kod |- | kak || kayapa kallahanî || Kayapa Kallahan || Kallahan || || kom bê kod |- | tne || tinoc kallahanî || Tinoc Kallahan || Kallahan || || kom bê kod |- | ktv || katuyiya rojhilatî || Eastern Katu || Katu || || kom bê kod |- | kuf || katuyiya rojavayî || Western Katu || Katu || || kom bê kod |- | eky || kayahiya rojhilatî || Eastern Kayah || Kayah || || kom bê kod |- | kyu || kayahiya rojavayî || Western Kayah || Kayah || || kom bê kod |- | kee || keresiya rojhilatî || Eastern Keres || Keres || || kom bê kod |- | kjq || keresiya rojavayî || Western Keres || Keres || || kom bê kod |- | kiw || kiwaiyiya bakur-rojhilat || Northeast Kiwai || Kiwai || || kom bê kod |- | kjd || kiwaiyiya başûrî || Southern Kiwai || Kiwai || || kom bê kod |- | kbk || grass koiariyî || Grass Koiari || Koiari || || kom bê kod |- | kpx || koiariyiya çiyayî || Mountain Koiari || Koiari || || kom bê kod |- | gjk || kaçi koliyî || Kachi Koli || Koli || || kom bê kod |- | kvx || parkari koliyî || Parkari Koli || Koli || || kom bê kod |- | kxp || wadiyara koliyî || Wadiyara Koli || Koli || || kom bê kod |- | kqo || krahniya rojhilatî || Eastern Krahn || Krahn || || kom bê kod |- | krw || krahniya rojavayî || Western Krahn || Krahn || || kom bê kod |- | ktj || plapo krumenî || Plapo Krumen || Krumen || || kom bê kod |- | pye || pye krumenî || Pye Krumen || Krumen || || kom bê kod |- | ted || tepo krumenî || Tepo Krumen || Krumen || || kom bê kod |- | lcp || lawayiya rojavayî || Western Lawa || Lawa || || kom bê kod |- | lwl || lawayiya rojhilatî || Eastern Lawa || Lawa || || kom bê kod |- | len || lencayiya honduranî || Honduran Lenca || Lenca || || kom bê kod |- | nai-sln || salvadoran lencayî || Salvadoran Lenca || Lenca || || kom bê kod |- | low || tampias lobuyî || Tampias Lobu || Lobu || || kom bê kod |- | ruu || lanas lobuyî || Lanas Lobu || Lobu || || kom bê kod |- | lbr || lorungiya bakurî || Northern Lorung || Lorung || || kom bê kod |- | lrr || lorungiya başûrî || Southern Lorung || Lorung || || kom bê kod |- | lrc || luriya bakurî || Northern Luri || Luri || || kom bê kod |- | luz || luriya başûrî || Southern Luri || Luri || || kom bê kod |- | mgp || magariya rojhilatî || Eastern Magar || Magar || || kom bê kod |- | mrd || magariya rojavayî || Western Magar || Magar || || kom bê kod |- | mjd || maiduyiya bakur-rojava || Northwest Maidu || Maidu || || kom bê kod |- | nmu || maiduyiya bakur-rojhilat || Northeast Maidu || Maidu || || kom bê kod |- | nsz || nisenanî || Nisenan || Maidu || || kom bê kod |- | vmv || maiduyiya geliyî || Valley Maidu || Maidu || || kom bê kod |- | mcu || mambilayiya kamerûnê || Cameroon Mambila || Mambila || || kom bê kod |- | mzk || mambilayiya nîjeryayî || Nigeria Mambila || Mambila || || kom bê kod |- | emk || maninkakaniya rojhilatî || Eastern Maninkakan || Maninkakan || || kom bê kod |- | mlq || malinkî || Western Maninkakan || Maninkakan || || kom bê kod |- | mwk || kita maninkakanî || Kita Maninkakan || Maninkakan || || kom bê kod |- | mqm || marquesana başûr || South Marquesan || Marquesan || || kom bê kod |- | mrq || marquesana bakur || North Marquesan || Marquesan || || kom bê kod |- | mss || maselayiya rojava || West Masela || Masela || || kom bê kod |- | mxz || maselayiya navendî || Central Masela || Masela || || kom bê kod |- | vme || maselayiya rojhilat || East Masela || Masela || || kom bê kod |- | maz || mazahuayiya navendî || Central Mazahua || Mazahua || || kom bê kod |- | mmc || miçoacan mazahuayî || Michoacán Mazahua || Mazahua || || kom bê kod |- | tpc || azoyú me'phaayî || Azoyú Me'phaa || Me'phaa || || kom bê kod |- | tpl || tlacoapa me'phaayî || Tlacoapa Me'phaa || Me'phaa || || kom bê kod |- | tpx || akatepekiya me'phaayî || Acatepec Me'phaa || Me'phaa || || kom bê kod |- | dro || daro-matu melanauyî || Daro-Matu Melanau || Melanau || || kom bê kod |- | mel || melanauyiya navendî || Central Melanau || Melanau || || kom bê kod |- | sdx || sibu melanauyî || Sibu Melanau || Melanau || || kom bê kod |- | emg || meohangiya rojhilatî || Eastern Meohang || Meohang || || kom bê kod |- | raf || meohangiya rojavayî || Western Meohang || Meohang || || kom bê kod |- | csi || coast miwokî || Coast Miwok || Miwok || || kom bê kod |- | csm || sierra mîwokiya navendî || Central Sierra Miwok || Miwok || || kom bê kod |- | lmw || miwokiya golî || Lake Miwok || Miwok || || kom bê kod |- | mkq || bay miwokî || Bay Miwok || Miwok || || kom bê kod |- | nsq || miwokiya sierraya bakurî || Northern Sierra Miwok || Miwok || || kom bê kod |- | pmw || plains miwokî || Plains Miwok || Miwok || || kom bê kod |- | skd || sierra miwokiya başûrî || Southern Sierra Miwok || Miwok || || kom bê kod |- | mco || coatlan mixeyî || Coatlán Mixe || Mixe || || kom bê kod |- | mir || isthmus mixeyî || Isthmus Mixe || Mixe || || kom bê kod |- | mto || totontepec mixeyî || Totontepec Mixe || Mixe || || kom bê kod |- | mxp || tlahuitoltepec mixeyî || Tlahuitoltepec Mixe || Mixe || || kom bê kod |- | mxq || juquila mixeyî || Juquila Mixe || Mixe || || kom bê kod |- | mzl || mazatlan mixeyî || Mazatlán Mixe || Mixe || || kom bê kod |- | neq || mixeyiya navendî ya bakur || North Central Mixe || Mixe || || kom bê kod |- | pxm || mixeyiya quetzaltepec || Quetzaltepec Mixe || Mixe || || kom bê kod |- | cmo || mnongiya navendî || Central Mnong || Mnong || || kom bê kod |- | mng || mnongiya rojhilatî || Eastern Mnong || Mnong || || kom bê kod |- | mnn || mnongiya başûrî || Southern Mnong || Mnong || || kom bê kod |- | kxi || keningau murutî || Keningau Murut || Murut || || kom bê kod |- | mvv || tagal murutî || Tagal Murut || Murut || || kom bê kod |- | sbr || sembakung murutî || Sembakung Murut || Murut || || kom bê kod |- | slg || selungaî murutî || Selungai Murut || Murut || || kom bê kod |- | srk || serudung murutî || Serudung Murut || Murut || || kom bê kod |- | tih || timugon murutî || Timugon Murut || Murut || || kom bê kod |- | kti || muyuyiya bakur || North Muyu || Muyu || || kom bê kod |- | kts || muyuyiya başûr || South Muyu || Muyu || || kom bê kod |- | jmn || makuri nagayî || Makuri Naga || Naga || || kom bê kod |- | kfw || kharam nagayî || Kharam Naga || Naga || || kom bê kod |- | kix || khiamniungan nagayî || Khiamniungan Naga || Naga || || kom bê kod |- | lpn || long phuri nagayî || Long Phuri Naga || Naga || || kom bê kod |- | lzn || leinong nagayî || Leinong Naga || Naga || || kom bê kod |- | nbe || konyak nagayî || Konyak Naga || Naga || || kom bê kod |- | nbi || mao nagayî || Mao Naga || Naga || || kom bê kod |- | nbu || rongmei nagayî || Rongmei Naga || Naga || || kom bê kod |- | nct || çothe nagayî || Chothe Naga || Naga || || kom bê kod |- | njh || lotha nagayî || Lotha Naga || Naga || || kom bê kod |- | njn || liangmai nagayî || Liangmai Naga || Naga || || kom bê kod |- | nkb || khoibu nagayî || Khoibu Naga || Naga || || kom bê kod |- | nkf || inpui nagayî || Inpui Naga || Naga || || kom bê kod |- | nkh || khezha nagayî || Khezha Naga || Naga || || kom bê kod |- | nki || thangal nagayî || Thangal Naga || Naga || || kom bê kod |- | nlq || lao nagayî || Lao Naga || Naga || || kom bê kod |- | nma || maram nagayî || Maram Naga || Naga || || kom bê kod |- | nme || mzieme nagayî || Mzieme Naga || Naga || || kom bê kod |- | nmf || tangkhul nagayî || Tangkhul Naga || Naga || || kom bê kod |- | nmh || monsang nagayî || Monsang Naga || Naga || || kom bê kod |- | nmo || moyon nagayî || Moyon Naga || Naga || || kom bê kod |- | nng || maring nagayî || Maring Naga || Naga || || kom bê kod |- | nnl || nagayiya rengmaya bakur || Northern Rengma Naga || Naga || || kom bê kod |- | npo || poçuri nagayî || Pochuri Naga || Naga || || kom bê kod |- | npu || puimei nagayî || Puimei Naga || Naga || || kom bê kod |- | nqq || kyan-karyaw nagayî || Kyan-Karyaw Naga || Naga || || kom bê kod |- | nre || rengma nagayiya başûr || Southern Rengma Naga || Naga || || kom bê kod |- | nri || çokri nagayî || Chokri Naga || Naga || || kom bê kod |- | nsa || sangtam nagayî || Sangtam Naga || Naga || || kom bê kod |- | nzm || zeme nagayî || Zeme Naga || Naga || || kom bê kod |- | pmx || poumei nagayî || Poumei Naga || Naga || || kom bê kod |- | pzn || para nagayî || Para Naga || Naga || || kom bê kod |- | tvt || tutsa nagayî || Tutsa Naga || Naga || || kom bê kod |- | umn || makyan nagayî || Makyan Naga || Naga || || kom bê kod |- | yim || yimçungru nagayî || Yimchungru Naga || Naga || || kom bê kod |- | nd || ndebeliya bakurî || Northern Ndebele || Ndebele || || kom bê kod |- | nr || ndebeliya başûrî || Southern Ndebele || Ndebele || || kom bê kod |- | nni || nuauluyiya bakur || North Nuaulu || Nuaulu || || kom bê kod |- | nxl || nuauluyiya başûr || South Nuaulu || Nuaulu || || kom bê kod |- | nnw || nuniya başûrî || Southern Nuni || Nuni || || kom bê kod |- | nuv || nuniya bakurî || Northern Nuni || Nuni || || kom bê kod |- | css || ohloniya başûr || Southern Ohlone || Ohlone || || kom bê kod |- | cst || ohloniya bakur || Northern Ohlone || Ohlone || || kom bê kod |- | ojb || ojibweyiya bakurî || Northwestern Ojibwa || Ojibwa || || kom bê kod |- | ojc || ojibweyiya navendî || Central Ojibwa || Ojibwa || || kom bê kod |- | ojg || ojibweyiya rojhilatî || Eastern Ojibwa || Ojibwa || || kom bê kod |- | ojs || ojibweyiya severnê || Severn Ojibwa || Ojibwa || || kom bê kod |- | ojw || ojibweyiya rojavayî || Western Ojibwa || Ojibwa || || kom bê kod |- | aun || molmo one || Molmo One || One || || kom bê kod |- | oin || oneyiya inebu || Inebu One || One || || kom bê kod |- | okk || oneyiya kwamtim || Kwamtim One || One || || kom bê kod |- | onk || oneyiya kabore || Kabore One || One || || kom bê kod |- | onr || oneyiya bakurî || Northern One || One || || kom bê kod |- | osu || oneyiya başûrî || Southern One || One || || kom bê kod |- | kmj || kumarbhag pahariayî || Kumarbhag Paharia || Paharia || || kom bê kod |- | mjt || sawriya pahariayî || Sawriya Paharia || Paharia || || kom bê kod |- | pce || ruçing palaungî || Ruching Palaung || Palaung || || kom bê kod |- | pll || şwe palaungî || Shwe Palaung || Palaung || || kom bê kod |- | rbb || rumaî palaungî || Rumai Palaung || Palaung || || kom bê kod |- | plc || palawanoyiya navendî || Central Palawano || Palawano || || kom bê kod |- | plv || palawanoyiya başûr-rojava || Southwest Palawano || Palawano || || kom bê kod |- | plw || palawanoyî || Brooke's Point Palawano || Palawano || || kom bê kod |- | pbs || pamiya navendî || Central Pame || Pame || || kom bê kod |- | pmq || pameyiya bakurî || Northern Pame || Pame || || kom bê kod |- | pmz || pameyiya başûrî || Southern Pame || Pame || || kom bê kod |- | pez || penaniya rojhilatî || Eastern Penan || Penan || || kom bê kod |- | pne || penaniya rojavayî || Western Penan || Penan || || kom bê kod |- | kju || kaşayayî || Kashaya || Pomo || || kom bê kod |- | peb || pomoyiya rojhilatî || Eastern Pomo || Pomo || || kom bê kod |- | pef || pomoyiya bakur-rojhilatî || Northeastern Pomo || Pomo || || kom bê kod |- | pej || pomoyiya bakurî || Northern Pomo || Pomo || || kom bê kod |- | peq || pomoyiya başûrî || Southern Pomo || Pomo || || kom bê kod |- | pom || pomoyiya başûr-rojhilatî || Southeastern Pomo || Pomo || || kom bê kod |- | poo || pomoyiya navendî || Central Pomo || Pomo || || kom bê kod |- | pbe || popolocayiya mezontla || Mezontla Popoloca || Popoloca || || kom bê kod |- | pbf || popolocayiya coyotepec || Coyotepec Popoloca || Popoloca || || kom bê kod |- | pca || popolocayiya santa ines ahuatempan || Santa Inés Ahuatempan Popoloca || Popoloca || || kom bê kod |- | pls || popolocayiya san marcos tlalcoyalco || San Marcos Tlalcoyalco Popoloca || Popoloca || || kom bê kod |- | poe || popolocayiya san juan atzingo || San Juan Atzingo Popoloca || Popoloca || || kom bê kod |- | pow || popolocayiya san felipe otlaltepec || San Felipe Otlaltepec Popoloca || Popoloca || || kom bê kod |- | pps || popolocayiya san luis temalacayuca || San Luís Temalacayuca Popoloca || Popoloca || || kom bê kod |- | plo || popolucayiya oluta || Oluta Popoluca || Popoluca || || kom bê kod |- | poi || popolucayiya highland || Highland Popoluca || Popoluca || || kom bê kod |- | poq || popolucayiya texistepec || Texistepec Popoluca || Popoluca || || kom bê kod |- | pos || popolucayiya sayula || Sayula Popoluca || Popoluca || || kom bê kod |- | pmi || pumiya bakurî || Northern Pumi || Pumi || || kom bê kod |- | pmj || pumiya başûrî || Southern Pumi || Pumi || || kom bê kod |- | kjp || pwoyiya rojhilatî || Eastern Pwo || Pwo || || kom bê kod |- | kjt || phrae pwoyî || Phrae Pwo || Pwo || || kom bê kod |- | pwo || pwoyiya rojavayî || Western Pwo || Pwo || || kom bê kod |- | pww || pwoyiya bakurî || Northern Pwo || Pwo || || kom bê kod |- | cng || qiangiya bakurî || Northern Qiang || Qiang || || kom bê kod |- | qxs || kiangiya başûrî || Southern Qiang || Qiang || || kom bê kod |- | rgs || roglayiya başûrî || Southern Roglai || Roglai || || kom bê kod |- | roc || cacgia roglaî || Cacgia Roglai || Roglai || || kom bê kod |- | rog || roglayiya bakurî || Northern Roglai || Roglai || || kom bê kod |- | hmb || humburi senniyî || Humburi Senni || Senni || || kom bê kod |- | ses || sonxayî || Koyraboro Senni || Senni || || kom bê kod |- | dyi || djiminiyî || Djimini || Senoufo || || kom bê kod |- | myk || mamara senoufoyî || Mamara || Senoufo || || kom bê kod |- | nfr || nafaanrayî || Nafaanra || Senoufo || || kom bê kod |- | plr || palaka senoufoyî || Palaka || Senoufo || || kom bê kod |- | sef || cebaara senoufoyî || Cebaara || Senoufo || || kom bê kod |- | sen || nanerige senoufoyî || Nanerige || Senoufo || || kom bê kod |- | sep || sìcìte senoufoyî || Sicite || Senoufo || || kom bê kod |- | seq || senara senoufoyî || Senara || Senoufo || || kom bê kod |- | sev || nyarafolo senoufoyî || Nyarafolo || Senoufo || || kom bê kod |- | shz || syenara senoufoyî || Syenara || Senoufo || || kom bê kod |- | spp || supyireyî || Supyire || Senoufo || || kom bê kod |- | tgw || tagwana senoufoyî || Tagwana || Senoufo || || kom bê kod |- | bks || masbate sorsogon || Masbate Sorsogon || Sorsogon || || kom bê kod |- | srv || waray sorsogonî || Waray Sorsogon || Sorsogon || || kom bê kod |- | sti || bulo stiengî || Bulo Stieng || Stieng || || kom bê kod |- | stt || budeh stiengî || Budeh Stieng || Stieng || || kom bê kod |- | laa || lapuyan subanunî || Lapuyan Subanun || Subanen || || kom bê kod |- | sfe || subanuna rojhilatî || Eastern Subanun || Subanen || || kom bê kod |- | skn || kolibugan subanonî || Kolibugan Subanon || Subanen || || kom bê kod |- | stb || subaneniya bakurî || Northern Subanen || Subanen || || kom bê kod |- | suc || subanoniya rojavayî || Western Subanon || Subanen || || kom bê kod |- | syb || subaneniya navendî || Central Subanen || Subanen || || kom bê kod |- | taj || tamangiya rojhilatî || Eastern Tamang || Tamang || || kom bê kod |- | tdg || tamangiya rojavayî || Western Tamang || Tamang || || kom bê kod |- | tge || tamangiya gorkhaya rojhilatî || Eastern Gorkha Tamang || Tamang || || kom bê kod |- | tmk || tamangiya bakur-rojavayî || || Tamang || || kom bê kod |- | tsf || tamangiya başûr-rojavayî || || Tamang || || kom bê kod |- | taa || tananaya jêrîn || Lower Tanana || Tanana || || kom bê kod |- | tau || tananaya jorîn || Upper Tanana || Tanana || || kom bê kod |- | tac || tarahumarayiya lowland || Lowland Tarahumara || Tarahumara || || kom bê kod |- | tar || tarahumarayiya navendî || Central Tarahumara || Tarahumara || || kom bê kod |- | tcu || tarahumarayiya başûr-rojhilatî || Southeastern Tarahumara || Tarahumara || || kom bê kod |- | thh || tarahumarayiya bakurî || Northern Tarahumara || Tarahumara || || kom bê kod |- | twr || tarahumarayiya başûr-rojavayî || Southwestern Tarahumara || Tarahumara || || kom bê kod |- | tre || taranganiya rojhilat || East Tarangan || Tarangan || || kom bê kod |- | txn || taranganiya rojava || West Tarangan || Tarangan || || kom bê kod |- | bnj || tawbuîdiya rojhilatî || Eastern Tawbuid || Tawbuid || || kom bê kod |- | twb || tawbuidiya rojavayî || Western Tawbuid || Tawbuid || || kom bê kod |- | tee || huehuetla tepehuayî || Huehuetla Tepehua || Tepehua || || kom bê kod |- | tpp || pisaflores tepehuayî || Pisaflores Tepehua || Tepehua || || kom bê kod |- | tpt || tlaçiçilco tepehuayî || Tlachichilco Tepehua || Tepehua || || kom bê kod |- | ntp || tepehuaniya bakurî || Northern Tepehuan || Tepehuan || || kom bê kod |- | stp || tepehuaniya başûr-rojhilat || Southeastern Tepehuan || Tepehuan || || kom bê kod |- | tla || tepehuaniya başûr-rojavayî || Southwestern Tepehuan || Tepehuan || || kom bê kod |- | itd || tidongiya başûrî || Southern Tidung || Tidung || || kom bê kod |- | ntd || tidongiya bakurî || Northern Tidung || Tidung || || kom bê kod |- | tcw || totonaciya tekpatlan || Tecpatlán Totonac || Totonac || || kom bê kod |- | tku || totonaciya nekaksaya jorîn || Upper Necaxa Totonac || Totonac || || kom bê kod |- | tlc || totonakiya misantla || Misantla Totonac || Totonac || || kom bê kod |- | tlp || totonakiya filomena mata-coahuitlan || Filomena Mata-Coahuitlán Totonac || Totonac || || kom bê kod |- | toc || totonakiya coyutla || Coyutla Totonac || Totonac || || kom bê kod |- | too || totonakiya xicotepec de juarez || Xicotepec de Juárez Totonac || Totonac || || kom bê kod |- | top || totonakiya papantla || Papantla Totonac || Totonac || || kom bê kod |- | tos || totonakiya çiyayî || Highland Totonac || Totonac || || kom bê kod |- | tqt || totonaciya rojavayî || Western Totonac || Totonac || || kom bê kod |- | tsp || toussianiya bakurî || Northern Toussian || Toussian || || kom bê kod |- | wib || toussianiya başûrî || Southern Toussian || Toussian || || kom bê kod |- | bhq || tukang besî başûr || South Tukang Besi || Tukang Besi || || kom bê kod |- | khc || tukang besiya bakur || North Tukang Besi || Tukang Besi || || kom bê kod |- | tbn || barro negro tuneboyî || Barro Negro Tunebo || Tunebo || || kom bê kod |- | tnb || tuneboyiya rojavayî || Western Tunebo || Tunebo || || kom bê kod |- | tnd || angosturas tuneboyî || Angosturas Tunebo || Tunebo || || kom bê kod |- | tuf || tuneboyiya navendî || Central Tunebo || Tunebo || || kom bê kod |- | tce || tutçoneyiya başûrî || Southern Tutchone || Tutchone || || kom bê kod |- | ttm || tutçoneyiya bakurî || Northern Tutchone || Tutchone || || kom bê kod |- | mcy || watutiya başûr || South Watut || Watut || || kom bê kod |- | mpl || watutiya navîn || Middle Watut || Watut || || kom bê kod |- | una || watutiya bakur || North Watut || Watut || || kom bê kod |- | zoc || zoqueyiya copainala || Copainalá Zoque || Zoque || || kom bê kod |- | zoh || zoqueyiya çimalapa || Chimalapa Zoque || Zoque || || kom bê kod |- | zoq || zoqueyiya tabasco || Tabasco Zoque || Zoque || || kom bê kod |- | zor || zoqueyiya rayón || Rayón Zoque || Zoque || || kom bê kod |- | zos || zoqueyiya francisco león || Francisco León Zoque || Zoque || || kom bê kod |- | aae || || Arbëresh Albanian || Albanian/Arbëresh || sq || ziman li ku tune |- | aat || || Arvanitika Albanian || Albanian/Arvanitika || sq || ziman li ku tune |- | aln || || Gheg Albanian || Albanian/Gheg || sq || ziman li ku tune |- | als || || Tosk Albanian || Albanian/Tosk || sq || ziman li ku tune |- | syr || || || Aramaic || arc || ziman li ku tune |- | ksh || || Kölsch || German || de || ziman li ku tune |- | pfl || || Palatine German || German || de || ziman li ku tune |- | kmr || || Northern Kurdish || Kurdish || ku || ziman li ku tune |- | fa-cls || || Classical Persian || Persian/Classical Persian || fa || ziman li ku tune |- | prs || || Dari || Persian/Dari || fa || ziman li ku tune |- | fa-ira || || Iranian Persian || Persian/Iranian Persian || fa || ziman li ku tune |} l1xbx0zye0e2wwfok3jz32r7yxyi4di 6794085 6794084 2026-10-14T21:26:23Z Wikihez 7344 Guarani and Nenets groups; Kurdish varieties left to Bi zaravayên din 6794085 wikitext text/x-wiki Komên zimanan ên ku [[MediaWiki:Gadget-wikt.translation-adder.js|amûra tercimeyan]] rêzên wergerê di bin wan de bi cih dike, li gorî tabloya en.wiktionary ([[:en:MediaWiki:Gadget-TranslationAdder-Data.js]]). Navên kurmancî ji [[Modul:ziman/code to canonical name.json]] û [[Modul:malbat/code to canonical name]] hatine. == Kom (650) == "Çawa": ''ziman'' — koma en.wiktionary ziman e; ''malbat'' — malbatek e ji [[Modul:malbat]]; ''ji rûpelên ku'' — ne di tabloya en.wiktionary de ye, lê rûpelên ku wisa dinivîsin. {| class="wikitable sortable" ! Kod !! Ziman !! en: ziman !! en: kom !! Kod (kom) !! Kom !! Çawa |- | goh || almaniya bilind a kevn || Old High German || German || de || almanî || ziman |- | gmh || almaniya bilind a navîn || Middle High German || German || de || almanî || ziman |- | gmw-ecg || almaniya navendî ya rojhilat || East Central German || German || de || almanî || ziman |- | bar || bayerî || Bavarian || German || de || almanî || ziman |- | gsw || elmanîşî || Alemannic German || German || de || almanî || ziman |- | vmf || frankiya rojhilatî || East Franconian || German || de || almanî || ziman |- | gmw-cfr || frankoniya navendî || Central Franconian || German || de || almanî || ziman |- | gmw-rfr || frankoniya rhine || Rhine Franconian || German || de || almanî || ziman |- | swg || swabî || Swabian || German || de || almanî || ziman |- | apk || apaçiya deştan || Plains Apache || Apache || apa || apaçî || malbat |- | apw || apaçiya rojava || Western Apache || Apache || apa || apaçî || malbat |- | apl || lipan || Lipan || Apache || apa || apaçî || malbat |- | apj || xîkarîyayî || Jicarilla || Apache || apa || apaçî || malbat |- | apm || çîrîkawayî || Chiricahua || Apache || apa || apaçî || malbat |- | aii || aramiya nû ya asûrî || Assyrian Neo-Aramaic || Aramaic || arc || aramî || ziman |- | bhn || aramiya nû ya botanî || Bohtan Neo-Aramaic || Aramaic || arc || aramî || ziman |- | sam || aramiya samarî || Samaritan Aramaic || Aramaic || arc || aramî || ziman |- | hrt || hertevinî || Hértevin || Aramaic || arc || aramî || ziman |- | huy || hulaulayî || Hulaulá || Aramaic || arc || aramî || ziman |- | kqd || koy sanjaq suratî || Koy Sanjaq Surat || Aramaic || arc || aramî || ziman |- | trg || lîşan didanî || Lishán Didán || Aramaic || arc || aramî || ziman |- | lsd || lîşana denî || Lishana Deni || Aramaic || arc || aramî || ziman |- | aij || lîşanîd noşan || Lishanid Noshan || Aramaic || arc || aramî || ziman |- | mid || mandaicî || Mandaic || Aramaic || arc || aramî || ziman |- | myz || mandaîkiya klasîk || Classical Mandaic || Aramaic || arc || aramî || ziman |- | lhs || mlahsö || Mlahsö || Aramaic || arc || aramî || ziman |- | bjf || neo-aramiya cihû ya barzanê || Barzani Jewish Neo-Aramaic || Aramaic || arc || aramî || ziman |- | amw || neo-aramiya rojavayî || Western Neo-Aramaic || Aramaic || arc || aramî || ziman |- | syn || senayayî || Senaya || Aramaic || arc || aramî || ziman |- | syc || siryaniya klasîk || Classical Syriac || Aramaic || arc || aramî || ziman |- | tru || turoyo || Turoyo || Aramaic || arc || aramî || ziman |- | cld || çaldean neo-aramaicî || Chaldean Neo-Aramaic || Aramaic || arc || aramî || ziman |- | inc-mas || asamiya navîn || Middle Assamese || Assamese || as || asamî || ziman |- | inc-oas || asamiya zû || Early Assamese || Assamese || as || asamî || ziman |- | oav || avariya kevn || Old Avar || Avar || av || avarî || ziman |- | inc-oaw || awadhiya kevn || Old Awadhi || Awadhi || awa || awadhî || ziman |- | ahp || aproumu aîzî || Apro || Aizi || kro-aiz || ayizî || malbat |- | ahm || mobumrîn aîzî || Mobu || Aizi || kro-aiz || ayizî || malbat |- | ahi || tîagbamrîn aîzî || Tiagba || Aizi || kro-aiz || ayizî || malbat |- | bqf || baga kaloumî || Baga Kaloum || Baga || alv-bag || baga || malbat |- | bgo || baga kogayî || Baga Koga || Baga || alv-bag || baga || malbat |- | bmd || baga mandurî || Baga Manduri || Baga || alv-bag || baga || malbat |- | bsp || baga sitemuyî || Baga Sitemu || Baga || alv-bag || baga || malbat |- | bsv || baga sobaneyî || Baga Sobané || Baga || alv-bag || baga || malbat |- | akb || angkola batakî || Angkola Batak || Batak || btk || batakî || malbat |- | btz || batakiya alas-kluet || Alas-Kluet Batak || Batak || btk || batakî || malbat |- | btd || batakiya dairî || Dairi Batak || Batak || btk || batakî || malbat |- | btx || batakiya karo || Karo Batak || Batak || btk || batakî || malbat |- | btm || batakiya mandaîlîng || Mandailing Batak || Batak || btk || batakî || malbat |- | bya || batakiya palawan || Palawan Batak || Batak || btk || batakî || malbat |- | bts || batakiya simalungun || Simalungun Batak || Batak || btk || batakî || malbat |- | bbc || bataktobayî || Toba Batak || Batak || btk || batakî || malbat |- | bfc || bayiya bakurî || Panyi Bai || Bai || sit-bai || bayî || malbat |- | bfs || bayiya başûrî || Southern Bai || Bai || sit-bai || bayî || malbat |- | bca || bayiya navendî || Central Bai || Bai || sit-bai || bayî || malbat |- | lay || lama baiyî || Lama Bai || Bai || sit-bai || bayî || malbat |- | inc-obn || bengaliya kevn || Old Bengali || Bengali || bn || bengalî || ziman |- | inc-mbn || bengaliya navîn || Middle Bengali || Bengali || bn || bengalî || ziman |- | kab || kabîlî || Kabyle || — || ber || berberî || ji rûpelên ku |- | rif || tarifitî || Tarifit || — || ber || berberî || ji rûpelên ku |- | shi || taşelhitî || Tashelhit || — || ber || berberî || ji rûpelên ku |- | tzm || temazîxtiya atlasa navendî || Central Atlas Tamazight || — || ber || berberî || ji rûpelên ku |- | shy || şawî || Tachawit || — || ber || berberî || ji rûpelên ku |- | obt || bretoniya kevn || Old Breton || Breton || br || bretonî || ziman |- | xbm || bretoniya navîn || Middle Breton || Breton || br || bretonî || ziman |- | obr || burmayiya kevn || Old Burmese || Burmese || my || burmayî || ziman |- | yln || buyangiya langnî || Langnian Buyang || Buyang || qfa-buy || buyangî || malbat |- | yzg || e'ma buyangî || E'ma Buyang || Buyang || qfa-buy || buyangî || malbat |- | enc || enî || En || Buyang || qfa-buy || buyangî || malbat |- | yrn || yerongî || Yerong || Buyang || qfa-buy || buyangî || malbat |- | bev || daloa beteyî || Daloa Bété || Bété || kro-bet || bétéyî || malbat |- | btg || gagnoa betî || Gagnoa Bété || Bété || kro-bet || bétéyî || malbat |- | bet || gûberowa beteyî || Guiberoua Bété || Bété || kro-bet || bétéyî || malbat |- | gmq-oda || danmarkiya kevn || Old Danish || Danish || da || danmarkî || ziman |- | gie || guebieyî || Guébie || Dida || kro-did || didayî || malbat |- | dic || lakota didayî || Lakota Dida || Dida || kro-did || didayî || malbat |- | gud || yocoboue didayî || Yocoboué Dida || Dida || kro-did || didayî || malbat |- | diw || dinkayiya bakur-rojavayî || Northwestern Dinka || Dinka || din || dinkayî || ziman |- | dip || dinkayiya bakur-rojhilatî || Northeastern Dinka || Dinka || din || dinkayî || ziman |- | dik || dinkayiya başûr-rojavayî || Southwestern Dinka || Dinka || din || dinkayî || ziman |- | dks || dinkayiya başûr-rojhilatî || Southeastern Dinka || Dinka || din || dinkayî || ziman |- | dib || dinkayiya navendî ya başûr || South Central Dinka || Dinka || din || dinkayî || ziman |- | aqd || amparî dogon || Ampari Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dti || ana tinga dogonî || Ana Tinga Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dbw || bankan tey dogonî || Bankan Tey Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dbt || ben teyî || Ben Tey || Dogon || qfa-dgn || dogonî || malbat |- | dbu || bondum dom dogonî || Bondum Dom Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dgb || bunogeyî || Bunoge || Dogon || qfa-dgn || dogonî || malbat |- | dbg || dogul domî || Dogul Dom || Dogon || qfa-dgn || dogonî || malbat |- | dds || donno so dogonî || Donno So Dogon || Dogon || qfa-dgn || dogonî || malbat |- | djm || jamsayî || Jamsay || Dogon || qfa-dgn || dogonî || malbat |- | dmb || mombo dogonî || Mombo Dogon || Dogon || qfa-dgn || dogonî || malbat |- | nzz || nanga dama dogonî || Nanga Dama Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dtu || tebul ure dogonî || Tebul Ure Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dtk || tene kan dogonî || Tene Kan Dogon || Dogon || qfa-dgn || dogonî || malbat |- | tde || tiranige diga dogonî || Tiranige Diga Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dto || tommo soyî || Tommo So || Dogon || qfa-dgn || dogonî || malbat |- | dtm || tomo kan dogonî || Tomo Kan Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dts || toro so dogonî || Toro So Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dtt || toro tegu dogonî || Toro Tegu Dogon || Dogon || qfa-dgn || dogonî || malbat |- | dym || yanda dogonî || Yanda Dogon || Dogon || qfa-dgn || dogonî || malbat |- | cbj || ede cabeyî || Ede Cabe || Ede || alv-ede || ede || malbat |- | ica || ede icayî || Ede Ica || Ede || alv-ede || ede || malbat |- | idd || ede idacayî || Ede Idaca || Ede || alv-ede || ede || malbat |- | ijj || ede ijeyî || Ede Ije || Ede || alv-ede || ede || malbat |- | nqg || ede nagoyî || Ede Nago || Ede || alv-ede || ede || malbat |- | nqk || kura ede nagoyî || Kura Ede Nago || Ede || alv-ede || ede || malbat |- | xkb || manigri-kambole edeyî nagoyî || Manigri-Kambolé Ede Nago || Ede || alv-ede || ede || malbat |- | ife || îfèî || Ifè || Ede || alv-ede || ede || malbat |- | abv || erebiya bahranî || Baharna Arabic || Arabic || ar || erebî || ziman |- | apc || erebiya bakûrê şamê || North Levantine Arabic || Arabic || ar || erebî || ziman |- | ajp || erebiya başûrê şamê || South Levantine Arabic || Arabic || ar || erebî || ziman |- | arq || erebiya cezayirî || Algerian Arabic || Arabic || ar || erebî || ziman |- | xaa || erebiya endulusî || Andalusian Arabic || Arabic || ar || erebî || ziman |- | ary || erebiya fasî || Moroccan Arabic || Arabic || ar || erebî || ziman |- | acw || erebiya hîcazî || Hijazi Arabic || Arabic || ar || erebî || ziman |- | acm || erebiya iraqî || Iraqi Arabic || Arabic || ar || erebî || ziman |- | pga || erebiya jubayê || Juba Arabic || Arabic || ar || erebî || ziman |- | afb || erebiya kendavî || Gulf Arabic || Arabic || ar || erebî || ziman |- | ayl || erebiya lîbyayî || Libyan Arabic || Arabic || ar || erebî || ziman |- | ayp || erebiya mezopotamyayî ya bakur || North Mesopotamian Arabic || Arabic || ar || erebî || ziman |- | arz || erebiya misrî || Egyptian Arabic || Arabic || ar || erebî || ziman |- | ars || erebiya necdî || Najdi Arabic || Arabic || ar || erebî || ziman |- | auz || erebiya ozbekî || Uzbeki Arabic || Arabic || ar || erebî || ziman |- | acy || erebiya qibrisî || Cypriot Arabic || Arabic || ar || erebî || ziman |- | ayn || erebiya sanaanî || Yemeni Arabic || Arabic || ar || erebî || ziman |- | apd || erebiya sudanî || Sudanese Arabic || Arabic || ar || erebî || ziman |- | abh || erebiya tajîkî || Tajiki Arabic || Arabic || ar || erebî || ziman |- | aeb || erebiya tunusî || Tunisian Arabic || Arabic || ar || erebî || ziman |- | acx || erebiya ummanî || Omani Arabic || Arabic || ar || erebî || ziman |- | shu || erebiya çadî || Chadian Arabic || Arabic || ar || erebî || ziman |- | ssh || erebiya şîhhî || Shihhi Arabic || Arabic || ar || erebî || ziman |- | adf || erebîya zufarî || Dhofari Arabic || Arabic || ar || erebî || ziman |- | mey || hassaniyayî || Hassaniya Arabic || Arabic || ar || erebî || ziman |- | xcl || ermeniya kevn || Old Armenian || Armenian || hy || ermenî || ziman |- | axm || ermeniya navîn || Middle Armenian || Armenian || hy || ermenî || ziman |- | fll || faliyiya bakur || North Fali || Fali || fli || faliyî || ziman |- | fal || faliyiya başûr || South Fali || Fali || fli || faliyî || ziman |- | peo || farisiya kevn || Old Persian || Persian || fa || farisî || ziman |- | pal || farisiya navîn || Middle Persian || Persian || fa || farisî || ziman |- | fro || fransiya kevn || Old French || French || fr || fransî || ziman |- | frm || fransiya navîn || Middle French || French || fr || fransî || ziman |- | frr || frîsiya bakur || North Frisian || Frisian || gmw-fri || frîsî || malbat |- | ofs || frîsiya kevn || Old Frisian || Frisian || gmw-fri || frîsî || malbat |- | stq || frîsiya saterlandî || Saterland Frisian || Frisian || gmw-fri || frîsî || malbat |- | fy || frîsî || West Frisian || Frisian || gmw-fri || frîsî || malbat |- | cib || ci gbeyî || Ci Gbe || Gbe || alv-gbe || gbe || malbat |- | gbh || defi gbeyî || Defi Gbe || Gbe || alv-gbe || gbe || malbat |- | gbs || gbesi gbeyî || Gbesi Gbe || Gbe || alv-gbe || gbe || malbat |- | kqk || kotafon gbeyî || Kotafon Gbe || Gbe || alv-gbe || gbe || malbat |- | mxl || maxi gbeyî || Maxi Gbe || Gbe || alv-gbe || gbe || malbat |- | sxw || saxwe gbeyî || Saxwe Gbe || Gbe || alv-gbe || gbe || malbat |- | tfi || tofin gbeyî || Tofin Gbe || Gbe || alv-gbe || gbe || malbat |- | wci || waci gbeyî || Waci Gbe || Gbe || alv-gbe || gbe || malbat |- | wem || weme gbeyî || Weme Gbe || Gbe || alv-gbe || gbe || malbat |- | xwe || xwela gbeyî || Xwela Gbe || Gbe || alv-gbe || gbe || malbat |- | xwl || xwla gbeyiya rojavayî || Western Xwla Gbe || Gbe || alv-gbe || gbe || malbat |- | gbx || xwla gbeyiya rojhilatî || Eastern Xwla Gbe || Gbe || alv-gbe || gbe || malbat |- | aou || a'ou || A'ou || Gelao || gio || gelaoyî || malbat |- | giq || gelaoyiya hêşîn || Green Gelao || Gelao || gio || gelaoyî || malbat |- | gir || gelaoyiya sor || Red Gelao || Gelao || gio || gelaoyî || malbat |- | giw || gelaoyiya spî || White Gelao || Gelao || gio || gelaoyî || malbat |- | giu || mulaoyî || Mulao || Gelao || gio || gelaoyî || malbat |- | gqu || qauyî || Qau || Gelao || gio || gelaoyî || malbat |- | gec || gboloo greboyî || Gboloo Grebo || Grebo || kro-grb || greboyî || malbat |- | gbo || greboyiya bakurî || Northern Grebo || Grebo || kro-grb || greboyî || malbat |- | gry || greboyiya barclayville || Barclayville Grebo || Grebo || kro-grb || greboyî || malbat |- | grj || greboyiya başûrî || Southern Grebo || Grebo || kro-grb || greboyî || malbat |- | grv || greboyiya navendî || Central Grebo || Grebo || kro-grb || greboyî || malbat |- | inc-ogu || gujaratiya kevn || Old Gujarati || Gujarati || gu || gujaratî || ziman |- | inc-mgu || gujaratiya navîn || Middle Gujarati || Gujarati || gu || gujaratî || ziman |- | oge || gurciya kevn || Old Georgian || Georgian || ka || gurcî || ziman |- | ggn || gurungiya rojhilatî || || Gurung || gvr || gurungiya rojavayî || ziman |- | gmq-ogt || gutnîşa kevn || Old Gutnish || Gutnish || gmq-gut || gutnişî || ziman |- | gui || guaraniyiya bolivyayî ya rojhilatî || Eastern Bolivian Guarani || Guarani || gn || guwaranî || ziman |- | gnw || guaraniyiya bolîvyaya rojavayî || Western Bolivian Guarani || Guarani || gn || guwaranî || ziman |- | gug || guaraniyiya paraguayanî || Paraguayan Guarani || Guarani || gn || guwaranî || ziman |- | gn-cls || guwaraniya klasîk || Classical Guarani || Guarani || gn || guwaranî || ziman |- | gun || mbya guaraniyî || Mbya Guarani || Guarani || gn || guwaranî || ziman |- | hdn || haidayiya bakurî || Northern Haida || Haida || hai || haidayî || ziman |- | hax || haidayiya başûrî || Southern Haida || Haida || hai || haidayî || ziman |- | inc-ohi || hindiya kevn || Old Hindi || Hindi || hi || hindî || ziman |- | gmw-jdt || danmarkiya jersey || Jersey Dutch || Dutch || nl || holendî || ziman |- | odt || holendiya kevn || Old Dutch || Dutch || nl || holendî || ziman |- | dum || holendiya navîn || Middle Dutch || Dutch || nl || holendî || ziman |- | ojp || japoniya kevn || Old Japanese || Japanese || ja || japonî || ziman |- | jas || javayiya kaledonî ya nû || New Caledonian Javanese || Javanese || jv || javayî || ziman |- | jvn || javayiya karîbî || Caribbean Javanese || Javanese || jv || javayî || ziman |- | kaw || javayiya kevn || Old Javanese || Javanese || jv || javayî || ziman |- | jiy || buyuan jinuoyî || Buyuan Jino || Jino || tbq-jin || jîno || malbat |- | jiu || youle jinuoyî || Youle Jino || Jino || tbq-jin || jîno || malbat |- | kll || kagan kalaganî || Kagan Kalagan || Kalagan || kqe || kalaganî || ziman |- | klg || tagakaulu kalaganî || Tagakaulu Kalagan || Kalagan || kqe || kalaganî || ziman |- | sxk || kalapuyayiya başûrî || Southern Kalapuya || Kalapuya || kyl || kalapuyayî || ziman |- | nrt || kalapuyayî bakurî || Northern Kalapuya || Kalapuya || kyl || kalapuyayî || ziman |- | doc || kamiya bakurî || Northern Kam || Kam || kdx || kamî || ziman |- | kmc || kamiya başûrî || Southern Kam || Kam || kdx || kamî || ziman |- | dra-okn || kannadayiya kevn || Old Kannada || Kannada || kn || kannadayî || ziman |- | dra-mkn || kannadayiya navîn || Middle Kannada || Kannada || kn || kannadayî || ziman |- | kza || karaboroyiya rojavayî || Western Karaboro || Karaboro || alv-krb || karaboro || malbat |- | xrb || karaboroyiya rojhilatî || Eastern Karaboro || Karaboro || alv-krb || karaboro || malbat |- | kvl || brek karenî || Brek Karen || Karen || kar || karenî || malbat |- | bwe || bwe karenî || Bwe Karen || Karen || kar || karenî || malbat |- | kvq || geba karenî || Geba Karen || Karen || kar || karenî || malbat |- | ghk || geko karenî || Geko Karen || Karen || kar || karenî || malbat |- | kvt || lahta karenî || Lahta Karen || Karen || kar || karenî || malbat |- | kxf || manumanaw karenî || Manumanaw Karen || Karen || kar || karenî || malbat |- | blk || pa'o karen || Pa'o Karen || Karen || kar || karenî || malbat |- | jkp || paku karen || Paku Karen || Karen || kar || karenî || malbat |- | ksw || s'gaw karenî || S'gaw Karen || Karen || kar || karenî || malbat |- | kvu || yinbaw karenî || Yinbaw Karen || Karen || kar || karenî || malbat |- | kvy || yintale karenî || Yintale Karen || Karen || kar || karenî || malbat |- | kxk || zayein karenî || Zayein Karen || Karen || kar || karenî || malbat |- | roa-oca || katalaniya kevn || Old Catalan || Catalan || ca || katalanî || ziman |- | bhv || bahauyî || Bahau || Kayan || pdu || kayanî || ziman |- | qwc || keçwayiya klasîk || Classical Quechua || Quechua || qu || keçwayî || ziman |- | kgj || gamale khamî || Gamale Kham || Kham || sit-kha || khamî || malbat |- | kjl || khamî || Western Parbate Kham || Kham || sit-kha || khamî || malbat |- | kif || parbate khamiya rojhilatî || Eastern Parbate Kham || Kham || sit-kha || khamî || malbat |- | kip || şeşi khamî || Sheshi Kham || Kham || sit-kha || khamî || malbat |- | kqs || kissiya bakurî || Northern Kissi || Kissi || alv-kis || kissî || malbat |- | kss || kissiyiya başûrî || Southern Kissi || Kissi || alv-kis || kissî || malbat |- | wni || komoriya ndzwanî || Ndzwani Comorian || Comorian || bnt-com || komorî || malbat |- | swb || komorî || Maore Comorian || Comorian || bnt-com || komorî || malbat |- | wlc || mwalî komorî || Mwali Comorian || Comorian || bnt-com || komorî || malbat |- | zdj || şîngazîcayî || Ngazidja Comorian || Comorian || bnt-com || komorî || malbat |- | kfd || korra koragayî || Korra Koraga || Koraga || dra-kor || koraga || malbat |- | vmd || mudu koragayî || Mudu Koraga || Koraga || dra-kor || koraga || malbat |- | oko || koreyiya kevn || Old Korean || Korean || ko || koreyî || ziman |- | ko-ear || koreyiya modern a pêşî || Early Modern Korean || Korean || ko || koreyî || ziman |- | okm || koreyiya navîn || Middle Korean || Korean || ko || koreyî || ziman |- | oco || korniya kevn || Old Cornish || Cornish || kw || kornî || ziman |- | cnx || korniya navîn || Middle Cornish || Cornish || kw || kornî || ziman |- | gkp || kpelleyiya gîneyî || Guinea Kpelle || Kpelle || kpe || kpelleyî || ziman |- | xpe || liberia kpelleyî || Liberia Kpelle || Kpelle || kpe || kpelleyî || ziman |- | atj || atîkamekwî || Atikamekw || Cree || cr || krî || ziman |- | crk || kriya bejayî || Plains Cree || Cree || cr || krî || ziman |- | crm || kriya mûsî || Moose Cree || Cree || cr || krî || ziman |- | crl || kriya rojhilat ya bakurî || Northern East Cree || Cree || cr || krî || ziman |- | crj || kriya rojhilat ya başûrî || Southern East Cree || Cree || cr || krî || ziman |- | csw || kriya swampî || Swampy Cree || Cree || cr || krî || ziman |- | cwd || kriya woods || Woods Cree || Cree || cr || krî || ziman |- | moe || montagnaîsî || Montagnais || Cree || cr || krî || ziman |- | nsk || naskapî || Naskapi || Cree || cr || krî || ziman |- | kzc || bondoukou kulangoyî || Bondoukou Kulango || Kulango || alv-kul || kulango || malbat |- | nku || bouna kulangoyî || Bouna Kulango || Kulango || alv-kul || kulango || malbat |- | cux || tepeuxila cuicatecî || Tepeuxila Cuicatec || Cuicatec || omq-cui || kuyikatekî || malbat |- | cut || teutila cuicatecî || Teutila Cuicatec || Cuicatec || omq-cui || kuyikatekî || malbat |- | yik || dongşanba laloyî || Dongshanba Lalo || Lalo || tbq-lal || laloyî || malbat |- | ywl || laluyiya rojavayî || Western Lalu || Lalo || tbq-lal || laloyî || malbat |- | yit || laluyiya rojhilatî || Eastern Lalu || Lalo || tbq-lal || laloyî || malbat |- | ywt || xişanba laloyî || Xishanba Lalo || Lalo || tbq-lal || laloyî || malbat |- | roa-ole || leoniya kevn || Old Leonese || Leonese || roa-leo || leonîzî || ziman |- | lia || limbayiya navendî-rojava || West-Central Limba || Limba || alv-lim || lîmba || malbat |- | lma || limbayiya rojhilat || East Limba || Limba || alv-lim || lîmba || malbat |- | olt || lîtwanyayiya kevn || Old Lithuanian || Lithuanian || lt || lîtwanî || ziman |- | bpq || banda malayî || Banda Malay || Malay || ms || malezî || ziman |- | kxd || bruneyî || Brunei Malay || Malay || ms || malezî || ziman |- | jax || jambî malayî || Jambi Malay || Malay || ms || malezî || ziman |- | lrt || larantuka malayî || Larantuka Malay || Malay || ms || malezî || ziman |- | btj || malayiya bakanîsî || Bacanese Malay || Malay || ms || malezî || ziman |- | mhp || malayiya balînîzî || Balinese Malay || Malay || ms || malezî || ziman |- | bve || malayiya berau || Berau Malay || Malay || ms || malezî || ziman |- | bvu || malayiya bukitî || Bukit Malay || Malay || ms || malezî || ziman |- | meo || malayiya kedah || Kedah Malay || Malay || ms || malezî || ziman |- | mqg || malayiya kota bangun kutai || Kota Bangun Kutai Malay || Malay || ms || malezî || ziman |- | mkn || malayiya kupang || Kupang Malay || Malay || ms || malezî || ziman |- | pse || malayiya navendî || Central Malay || Malay || ms || malezî || ziman |- | pmy || malayiya papuayê || Papuan Malay || Malay || ms || malezî || ziman |- | mfa || malayiya pattani || Pattani Malay || Malay || ms || malezî || ziman |- | msi || malayiya sabah || Sabah Malay || Malay || ms || malezî || ziman |- | xmm || manado malayî || Manado Malay || Malay || ms || malezî || ziman |- | zmi || negeri sembilan malayî || Negeri Sembilan Malay || Malay || ms || malezî || ziman |- | poz-sml || sarawak malay || Sarawak Malay || Malay || ms || malezî || ziman |- | vkt || tenggarong kutai malayî || Tenggarong Kutai Malay || Malay || ms || malezî || ziman |- | poz-ter || terengganu malay || Terengganu Malay || Malay || ms || malezî || ziman |- | mku || konyanka maninkayî || Konyanka Maninka || Mandingo || man || mandingoyî || ziman |- | mnk || mandinkayî || Mandinka || Mandingo || man || mandingoyî || ziman |- | msc || sankaran maninkayî || Sankaran Maninka || Mandingo || man || mandingoyî || ziman |- | omp || manipuriya kevn || Old Manipuri || Manipuri || mni || manipurî || ziman |- | bpy || manîpûriya bîşnûpriyayî || Bishnupriya Manipuri || Manipuri || mni || manipurî || ziman |- | msm || agusan manoboyî || Agusan Manobo || Manobo || mno || manoboyî || malbat |- | atd || ata manobo || Ata Manobo || Manobo || mno || manoboyî || malbat |- | mbb || bukidnon manoboyiya rojavayî || Western Bukidnon Manobo || Manobo || mno || manoboyî || malbat |- | mkx || cinamiguin manoboyî || Cinamiguin Manobo || Manobo || mno || manoboyî || malbat |- | mbd || dibabawon manoboyî || Dibabawon Manobo || Manobo || mno || manoboyî || malbat |- | mbi || ilianen manoboyî || Ilianen Manobo || Manobo || mno || manoboyî || malbat |- | mta || kotabato manoboyî || Cotabato Manobo || Manobo || mno || manoboyî || malbat |- | mbt || matigsalug manoboyî || Matigsalug Manobo || Manobo || mno || manoboyî || malbat |- | obo || obo manoboyî || Obo Manobo || Manobo || mno || manoboyî || malbat |- | mqk || rajah kabunsuwan manoboyî || Rajah Kabunsuwan Manobo || Manobo || mno || manoboyî || malbat |- | mbs || saranganî manoboyî || Sarangani Manobo || Manobo || mno || manoboyî || malbat |- | mns-nor || mansiya bakurî || Northern Mansi || Mansi || mns || mansî || malbat |- | mns-sou || mansiya başûrî || Southern Mansi || Mansi || mns || mansî || malbat |- | mns-cen || mansiya merkezî || Central Mansi || Mansi || mns || mansî || malbat |- | omr || marathiya kevn || Old Marathi || Marathi || mr || maratî || ziman |- | daq || dandami mariayî || Dandami Maria || Maria || mds || mariayî || ziman |- | mrr || hill mariayî || Hill Maria || Maria || mds || mariayî || ziman |- | mrj || mariya rojavayî || Western Mari || Mari || chm || marî || malbat |- | mhr || mariya rojhilatî || Eastern Mari || Mari || chm || marî || malbat |- | mzi || ixcatlan mazatecî || Ixcatlán Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | vmy || mazateciya ayautla || Ayautla Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | mau || mazateciya huautla || Huautla Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | maj || mazateciya jalapa de diaz || Jalapa de Díaz Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | maa || mazateciya san jerónimo tecóatl || San Jerónimo Tecóatl Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | maq || mazateciya çiquihuitlan || Chiquihuitlán Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | vmz || mazatekiya mazatlanî || Mazatlán Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | pbm || mazatekiya puebla || Puebla Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | vmp || mazatekiya soyaltepec || Soyaltepec Mazatec || Mazatec || omq-maz || mazatekanî || malbat |- | ohu || mecariya kevn || Old Hungarian || Hungarian || hu || mecarî || ziman |- | xme-old || mediya kevn || Old Median || Median || xme || medî || malbat |- | xme-mid || mediya navîn || Middle Median || Median || xme || medî || malbat |- | egx-dem || demotîkî || Demotic Egyptian || Egyptian || egy || misrî || ziman |- | cmg || mongoliya klasîk || Classical Mongolian || Mongolian || mn || mongolî || ziman |- | omx || moniya kevn || Old Mon || Mon || mnw || monî || ziman |- | mkh-mmn || moniya navîn || Middle Mon || Mon || mnw || monî || ziman |- | mnw-tha || thai mon || Thai Mon || Mon || mnw || monî || ziman |- | ymx || mujiya bakurî || Northern Muji || Muji || tbq-muj || mujî || malbat |- | ymc || mujiya başûrî || Southern Muji || Muji || tbq-muj || mujî || malbat |- | ymq || qila mujî || Qila Muji || Muji || tbq-muj || mujî || malbat |- | fmu || muriayiya rojava ya dûr || Far Western Muria || Muria || dra-mur || muriya || malbat |- | mut || muriayiya rojavayî || Western Muria || Muria || dra-mur || muriya || malbat |- | emu || muriayiya rojhilatî || Eastern Muria || Muria || dra-mur || muriya || malbat |- | jmx || juxtlahuaca mixteciya rojavayî || Western Juxtlahuaca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mim || mixteciya alacatlatzala || Alacatlatzala Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mbz || mixteciya amoltepek || Amoltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mip || mixteciya apasco-apoala || Apasco-Apoala Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mib || mixteciya atatlahukayê || Atatláhuca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | miy || mixteciya ayutla || Ayutla Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | miu || mixteciya cacaloxtepec || Cacaloxtepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | miz || mixteciya coatzospan || Coatzospan Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxs || mixteciya huitepec || Huitepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mce || mixteciya itundujia || Itundujia Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxt || mixteciya jamiltepec || Jamiltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxv || mixteciya metlatónoc || Metlatónoc Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mix || mixteciya mixtepec || Mixtepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxy || mixteciya noçixtlana başûr-rojhilatî || Southeastern Nochixtlán Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxa || mixteciya oaxakaya bakur-rojava || Northwest Oaxaca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mie || mixteciya ocotepekî || Ocotepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mil || mixteciya peñoles || Peñoles Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mio || mixteciya pinotepa nacional || Pinotepa Nacional Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mit || mixteciya pueblaya başûr || Southern Puebla Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mjc || mixteciya san juan colorado || San Juan Colorado Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mig || mixteciya san miguel el grande || San Miguel el Grande Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mdv || mixteciya santa lucia monteverde || Santa Lucía Monteverde Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mza || mixteciya santa maria zacatepec || Santa María Zacatepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mks || mixteciya silacayoapan || Silacayoapan Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mxb || mixteciya tezoatlan || Tezoatlán Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mtx || mixteciya tidaa || Tidaá Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | meh || mixteciya tlaxiacoya başûr-rojavayî || Southwestern Tlaxiaco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mqh || mixteciya tlazoyaltepek || Tlazoyaltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mtu || mixteciya tututepekî || Tututepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mpm || mixteciya yosondúa || Yosondúa Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mvg || mixteciya yucuañe || Yucuañe Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mab || mixteciya yutanduçî || Yutanduchi Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mih || mixteciya çayuco || Chayuco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | mii || mixteciya çigmecatitlan || Chigmecatitlán Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xta || mixtekiya alcozauca || Alcozauca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtu || mixtekiya cuyamecalco || Cuyamecalco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtd || mixtekiya diuxi-tilantongo || Diuxi-Tilantongo Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmj || mixtekiya ixtayutla || Ixtayutla Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmc || mixtekiya juxtlahuaca || Juxtlahuaca Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtm || mixtekiya magdalena peñasco || Magdalena Peñasco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmm || mixtekiya mitlatongo || Mitlatongo Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtj || mixtekiya san juan teita || San Juan Teita Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtp || mixtekiya san miguel piedras || San Miguel Piedras Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xts || mixtekiya sindihui || Sindihui Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xti || mixtekiya sinicahua || Sinicahua Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmq || mixtekiya soyaltepec || Soyaltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtt || mixtekiya tacahua || Tacahua Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | vmx || mixtekiya tamazola || Tamazola Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | omq-tel || mixtekiya teposcolula || Teposcolula Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtl || mixtekiya tijaltepec || Tijaltepec Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtn || mixtekiya tlaxiakoya bakurî || Northern Tlaxiaco Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xty || mixtekiya yoloxoçitl || Yoloxochitl Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | xtb || mixtekiya çazumba || Chazumba Mixtec || Mixtec || omq-mxt || mîkstekî || malbat |- | azd || durango nahuatliya rojavayî || Eastern Durango Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | azn || durango nahuatliya rojavayî ya jêrîn || Western Durango Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ncj || nahwatliya bakurê puebla || Northern Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhk || nahwatliya cosoleacaque || Cosoleacaque Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ngu || nahwatliya guerrero || Guerrero Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nch || nahwatliya huastecaya navendî || Central Huasteca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhw || nahwatliya huastecaya rojavayî || Western Huasteca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhe || nahwatliya huastekaya rojhilatî || Eastern Huasteca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhq || nahwatliya huaxcaleca || Huaxcaleca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nci || nahwatliya klasîk || Classical Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | naz || nahwatliya koatepek || Coatepec Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhx || nahwatliya mekayapan || Mecayapan Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhm || nahwatliya morelos || Morelos Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ncl || nahwatliya mîçoakanê || Michoacán Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhn || nahwatliya navendî || Central Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhy || nahwatliya oaxakaya bakurî || Northern Oaxaca Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nht || nahwatliya ometepec || Ometepec Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nlv || nahwatliya orizaba || Orizaba Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhp || nahwatliya pajapan || Pajapan Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | npl || nahwatliya pueblaya başûr-rojhilatî || Southeastern Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | ncx || nahwatliya pueblaya navendî || Central Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhz || nahwatliya santa maria la alta || Santa María La Alta Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nsu || nahwatliya sierra negra || Sierra Negra Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhc || nahwatliya tabasco || Tabasco Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhv || nahwatliya temascaltepec || Temascaltepec Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhg || nahwatliya tetelcingo || Tetelcingo Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nuz || nahwatliya tlamacazapa || Tlamacazapa Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nhi || nahwatliya zacatlan-ahuacatlan-tepetzintla || Zacatlán-Ahuacatlán-Tepetzintla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | azz || puebla nahuatliya çiyayî || Highland Puebla Nahuatl || Nahuatl || nah || nahwatlî || ziman |- | nds-de || almaniya jêrîn a almanî || German Low German || Low German || nds || nedersaksî || ziman |- | gml || almaniya jêrîn a navîn || Middle Low German || Low German || nds || nedersaksî || ziman |- | nds-nl || saksoniya jêrîn ya holendî || Dutch Low Saxon || Low German || nds || nedersaksî || ziman |- | syd-fne || nenetsiya daristanî || || Nenets || yrk || nenetsiya tundra || ziman |- | nwc || newariya klasîk || Classical Newar || Newar || new || newarî || ziman |- | nwx || newariya navîn || Middle Newar || Newar || new || newarî || ziman |- | nb || norweciya bokmålî || Norwegian Bokmål || Norwegian/Bokmål || no || norwecî || ziman |- | gmq-mno || norweciya navîn || Middle Norwegian || Norwegian || no || norwecî || ziman |- | nn || norweciya nînorskî || Norwegian Nynorsk || Norwegian/Nynorsk || no || norwecî || ziman |- | nsl || zimanê destan ê norwecî || Norwegian Sign Language || Norwegian || no || norwecî || ziman |- | ngb || ngbandiya bakurî || Northern Ngbandi || Ngbandi || nic-ngd || nîgbandî || malbat |- | nbw || ngbandiya başûrî || Southern Ngbandi || Ngbandi || nic-ngd || nîgbandî || malbat |- | caq || car nicobareseyî || Car Nicobarese || Nicobarese || aav-nic || nîkobarî || malbat |- | ncb || nicobariya navendî || Central Nicobarese || Nicobarese || aav-nic || nîkobarî || malbat |- | nik || nikobariya başûrî || Southern Nicobarese || Nicobarese || aav-nic || nîkobarî || malbat |- | yiv || nisuyiya bakurî || Northern Nisu || Nisu || tbq-nis || nîsu || malbat |- | nsv || nisuyiya başûr-rojavayî || || Nisu || tbq-nis || nîsu || malbat |- | nsd || nisuyiya başûrî || Southern Nisu || Nisu || tbq-nis || nîsu || malbat |- | nos || nisuyiya rojhilatî || Eastern Nisu || Nisu || tbq-nis || nîsu || malbat |- | pro || oksîtaniya kevn || Old Occitan || Occitan || oc || oksîtanî || ziman |- | ort || oriyayiya adivasî || Adivasi Odia || Odia || or || oriyayî || ziman |- | inc-oor || oriyayiya kevn || Old Odia || Odia || or || oriyayî || ziman |- | inc-mor || oriyayiya navîn || Middle Odia || Odia || or || oriyayî || ziman |- | ots || otomiya estado de meksîka || Estado de México Otomi || Otomi || oto-otm || otomî || malbat |- | otm || otomiya highland a rojhilatî || Eastern Highland Otomi || Otomi || oto-otm || otomî || malbat |- | otz || otomiya ixtenco || Ixtenco Otomi || Otomi || oto-otm || otomî || malbat |- | ote || otomiya mezquital || Mezquital Otomi || Otomi || oto-otm || otomî || malbat |- | otq || otomiya queretaro || Querétaro Otomi || Otomi || oto-otm || otomî || malbat |- | ott || otomiya temoaya || Temoaya Otomi || Otomi || oto-otm || otomî || malbat |- | otn || otomiya tenango || Tenango Otomi || Otomi || oto-otm || otomî || malbat |- | otx || otomiya texcatepec || Texcatepec Otomi || Otomi || oto-otm || otomî || malbat |- | otl || otomiya tilapa || Tilapa Otomi || Otomi || oto-otm || otomî || malbat |- | glh || paşayiya bakur-rojavayî || Northwest Pashayi || Pashayi || inc-pas || paşayî || malbat |- | aee || paşayiya bakur-rojhilat || Northeast Pashayi || Pashayi || inc-pas || paşayî || malbat |- | psh || paşayiya başûr-rojava || Southwest Pashayi || Pashayi || inc-pas || paşayî || malbat |- | psi || paşayiya başûr-rojhilat || Southeast Pashayi || Pashayi || inc-pas || paşayî || malbat |- | ypn || ani phowayî || Ani Phowa || Phowa || tbq-phw || phowa || malbat |- | yhl || hlepho phowayî || Hlepho Phowa || Phowa || tbq-phw || phowa || malbat |- | ypb || labo phowayî || Labo Phowa || Phowa || tbq-phw || phowa || malbat |- | zlw-opl || poloniya kevn || Old Polish || Polish || pl || polonî || ziman |- | vkp || kreyoliya portugalî ya korlai || Korlai Creole Portuguese || Portuguese || pt || portugalî || ziman |- | crp-mpp || pidginiya portugalî ya macau || Macau Pidgin Portuguese || Portuguese || pt || portugalî || ziman |- | inc-ash || prakrîtiya aşokanît || Ashokan Prakrit || Prakrit || pra || prakrît || ziman |- | inc-kam || prakrîtiya kamarupî || Kamarupi Prakrit || Prakrit || pra || prakrît || ziman |- | pra-niy || prakrîtiya niya || Niya Prakrit || Prakrit || pra || prakrît || ziman |- | pmu || mirpur panjabî || || Punjabi || pa || puncabî || ziman |- | inc-opa || puncabiya kevn || Old Punjabi || Punjabi || pa || puncabî || ziman |- | rmn || romaniya balkanî || Balkan Romani || Romani || rom || romanî || ziman |- | rml || romaniya baltîkî || Baltic Romani || Romani || rom || romanî || ziman |- | rmf || romaniya kalo fînî || Kalo Finnish Romani || Romani || rom || romanî || ziman |- | rmc || romaniya karpatyayî || Carpathian Romani || Romani || rom || romanî || ziman |- | rmo || romaniya sinte || Sinte Romani || Romani || rom || romanî || ziman |- | rmu || romaniya tavringer || Tavringer Romani || Romani || rom || romanî || ziman |- | rmy || romaniya vlaks || Vlax Romani || Romani || rom || romanî || ziman |- | rmw || romaniya welşî || Welsh Romani || Romani || rom || romanî || ziman |- | slm || pangutaran samayî || Pangutaran Sama || Sama || smd || samayî || ziman |- | ssb || samayiya başûrî || Southern Sama || Sama || smd || samayî || ziman |- | sml || samayiya navendî || Central Sama || Sama || smd || samayî || ziman |- | stj || matya samoyî || Matya Samo || Samo || smq || samoyî || ziman |- | sym || maya samoyî || Maya Samo || Samo || smq || samoyî || ziman |- | sbd || samoyiya başûrî || Southern Samo || Samo || smq || samoyî || ziman |- | sjk || kemi samî || Kemi Sami || Sami || smi || samîkî || malbat |- | sje || pite samî || Pite Sami || Sami || smi || samîkî || malbat |- | sia || samiya akkalayî || Akkala Sami || Sami || smi || samîkî || malbat |- | se || samiya bakurî || Northern Sami || Sami || smi || samîkî || malbat |- | sma || samiya başûr || Southern Sami || Sami || smi || samîkî || malbat |- | sjd || samiya kildînî || Kildin Sami || Sami || smi || samîkî || malbat |- | smj || samiya lule || Lule Sami || Sami || smi || samîkî || malbat |- | sms || samiya skoltî || Skolt Sami || Sami || smi || samîkî || malbat |- | smn || samiya înarî || Inari Sami || Sami || smi || samîkî || malbat |- | sjt || ter samî || Ter Sami || Sami || smi || samîkî || malbat |- | sju || ume samî || Ume Sami || Sami || smi || samîkî || malbat |- | sdn || galûrîsî || Gallurese || — || sc || sardînî || ji rûpelên ku |- | sdc || sassarîsî || Sassarese || — || sc || sardînî || ji rûpelên ku |- | sel-nor || selkupiya bakurî || Northern Selkup || Selkup || sel || selkupî || malbat |- | sel-sou || selkupiya başûrî || Southern Selkup || Selkup || sel || selkupî || malbat |- | gmw-msc || skotlendiya navîn || Middle Scots || Scots || sco || skotî || ziman |- | scs || slaveyiya bakur || North Slavey || Slavey || den || slaveyî || ziman |- | xsl || slaveyiya başûr || South Slavey || Slavey || den || slaveyî || ziman |- | zls-chs || slavonîka dêrê || Church Slavonic || Old Church Slavonic || cu || slaviya kevn || ziman |- | zlw-osk || slovakiya kevn || Old Slovak || Slovak || sk || slovakî || ziman |- | hsb || sorbiya jorîn || Upper Sorbian || Sorbian || wen || sorbî || malbat |- | dsb || sorbiya jêrîn || Lower Sorbian || Sorbian || wen || sorbî || malbat |- | osp || spaniya kevn || Old Spanish || Spanish || es || spanî || ziman |- | osn || sundaniya kevn || Old Sundanese || Sundanese || su || sundanî || ziman |- | gmq-osw || swêdiya kevn || Old Swedish || Swedish || sv || swêdî || ziman |- | oty || tamîliya kevn || Old Tamil || Tamil || ta || tamîlî || ziman |- | pht || phu tayî || Phu Thai || Thai || th || tayî || ziman |- | sou || taiya başûrî || Southern Thai || Thai || th || tayî || ziman |- | nod || tayiya bakurî || Northern Thai || Thai || th || tayî || ziman |- | dra-ote || teluguya kevn || Old Telugu || Telugu || te || telûgûyî || ziman |- | thl || dangaura tharuyî || Dangaura Tharu || Tharu || inc-tha || tharu || malbat |- | tkt || kathoriya tharuyî || Kathoriya Tharu || Tharu || inc-tha || tharu || malbat |- | thq || koçila tharuyî || Kochila Tharu || Tharu || inc-tha || tharu || malbat |- | thr || rana tharuyî || Rana Tharu || Tharu || inc-tha || tharu || malbat |- | the || çitwania tharuyî || Chitwania Tharu || Tharu || inc-tha || tharu || malbat |- | ota || osmanî || Ottoman Turkish || Turkish || tr || tirkî || ziman |- | bgx || tirkiya gagawizî ya balkanan || Balkan Gagauz Turkish || Turkish || tr || tirkî || ziman |- | trk-oat || tirkiya kevn ya anadolî || Old Anatolian Turkish || Turkish || tr || tirkî || ziman |- | kmz || tirkiya xorasanê || Khorasani Turkish || Turkish || tr || tirkî || ziman |- | trc || trîkuyiya copala || Copala Triqui || Triqui || omq-tri || trîkiyî || malbat |- | trq || trîkuyiya san martin itunyoso || San Martín Itunyoso Triqui || Triqui || omq-tri || trîkiyî || malbat |- | trs || trîkuyiya çicahuaxtla || Chicahuaxtla Triqui || Triqui || omq-tri || trîkiyî || malbat |- | tji || tujiayiya bakurî || Northern Tujia || Tujia || sit-tja || tujya || malbat |- | tjs || tujyayiya başûrî || Southern Tujia || Tujia || sit-tja || tujya || malbat |- | xct || tibetiya klasîk || Classical Tibetan || Tibetan || bo || tîbetî || ziman |- | otb || tîbetiya kevn || Old Tibetan || Tibetan || bo || tîbetî || ziman |- | mkh-mvi || viyetnamiya navîn || Middle Vietnamese || Vietnamese || vi || viyetnamî || ziman |- | owl || weylsiya kevn || Old Welsh || Welsh || cy || weylsî || ziman |- | wlm || weylsiya navîn || Middle Welsh || Welsh || cy || weylsî || ziman |- | gxx || weya başûrî || Wè Southern || Wee || kro-wee || wiyî || malbat |- | wec || wè westernî || Wè Western || Wee || kro-wee || wiyî || malbat |- | kca-nor || xantiya bakurî || Northern Khanty || Khanty || kca || xantî || ziman |- | kca-sou || xantiya başûrî || Southern Khanty || Khanty || kca || xantî || ziman |- | kca-eas || xantiya rojhilatî || Eastern Khanty || Khanty || kca || xantî || ziman |- | kxm || ximeriya bakurî || Northern Khmer || Khmer || km || ximêrî || ziman |- | okz || ximeriya kevn || Old Khmer || Khmer || km || ximêrî || ziman |- | xhm || ximeriya navîn || Middle Khmer || Khmer || km || ximêrî || ziman |- | yok-gsy || gaşowu || Gashowu Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-ply || palewyamî || Palewyami Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-bvy || yokutiya buena vîsta || Buena Vista Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-dly || yokutiya deltayê || Delta Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-nvy || yokutiya geliyê bakurî || Northern Valley Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-svy || yokutiya geliyê başûrî || Southern Valley Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-tky || yokutiya tule-kaweah || Tule-Kaweah Yokuts || Yokuts || yok || yokutsanî || malbat |- | yok-kry || yokutiya çema kings || Kings River Yokuts || Yokuts || yok || yokutsanî || malbat |- | ykg || yukaghiriya bakurî || Northern Yukaghir || Yukaghir || qfa-yuk || yukaghirî || malbat |- | yux || yukaghiriya başûrî || Southern Yukaghir || Yukaghir || qfa-yuk || yukaghirî || malbat |- | grk-ita || grîkoyî || Italiot Greek || Greek || el || yûnanî || ziman |- | tsd || tsakonî || Tsakonian || Greek || el || yûnanî || ziman |- | yej || yevanîkî || Yevanic || Greek || el || yûnanî || ziman |- | grk-cal || yûnaniya kalabrî || Calabrian Greek || Greek || el || yûnanî || ziman |- | cpg || yûnaniya kapadokyayî || Cappadocian Greek || Greek || el || yûnanî || ziman |- | grc || yûnaniya kevn || Ancient Greek || Greek || el || yûnanî || ziman |- | grk-mar || yûnaniya mariupol || Mariupol Greek || Greek || el || yûnanî || ziman |- | gmy || yûnaniya mîkenî || Mycenaean Greek || Greek || el || yûnanî || ziman |- | pnt || yûnaniya pontî || Pontic Greek || Greek || el || yûnanî || ziman |- | zpd || zapoteciya ixtlana başûr-rojhilatî || Southeastern Ixtlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zsr || zapoteciya rincona başûrî || Southern Rincon Zapotec || Zapotec || zap || zapotekî || ziman |- | zaq || zapotekiya aloapam || Aloápam Zapotec || Zapotec || zap || zapotekî || ziman |- | zpo || zapotekiya amatlan || Amatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zoo || zapotekiya asunción mixtepec || Asunción Mixtepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zaf || zapotekiya ayoquesco || Ayoquesco Zapotec || Zapotec || zap || zapotekî || ziman |- | zad || zapotekiya cajonos || Cajonos Zapotec || Zapotec || zap || zapotekî || ziman |- | zca || zapotekiya coatecas altas || Coatecas Altas Zapotec || Zapotec || zap || zapotekî || ziman |- | zps || zapotekiya coatlan || Coatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zpp || zapotekiya el alto || El Alto Zapotec || Zapotec || zap || zapotekî || ziman |- | zte || zapotekiya elotepec || Elotepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zpg || zapotekiya guevea de humboldt || Guevea de Humboldt Zapotec || Zapotec || zap || zapotekî || ziman |- | xzp || zapotekiya kevnare || Ancient Zapotec || Zapotec || zap || zapotekî || ziman |- | ztl || zapotekiya lapaguia-guivini || Lapaguía-Guivini Zapotec || Zapotec || zap || zapotekî || ziman |- | zpa || zapotekiya laçiguiri || Lachiguiri Zapotec || Zapotec || zap || zapotekî || ziman |- | zpl || zapotekiya laçixio || Lachixío Zapotec || Zapotec || zap || zapotekî || ziman |- | ztp || zapotekiya loxiça || Loxicha Zapotec || Zapotec || zap || zapotekî || ziman |- | zpy || zapotekiya mazaltepec || Mazaltepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zaw || zapotekiya mitla || Mitla Zapotec || Zapotec || zap || zapotekî || ziman |- | zpm || zapotekiya mixtepec || Mixtepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zac || zapotekiya ocotlan || Ocotlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zao || zapotekiya ozolotepec || Ozolotepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zpe || zapotekiya petapa || Petapa Zapotec || Zapotec || zap || zapotekî || ziman |- | zpj || zapotekiya quiavicuzas || Quiavicuzas Zapotec || Zapotec || zap || zapotekî || ziman |- | ztq || zapotekiya quioquitani-quieri || Quioquitani-Quierí Zapotec || Zapotec || zap || zapotekî || ziman |- | zar || zapotekiya rincón || Rincón Zapotec || Zapotec || zap || zapotekî || ziman |- | ztm || zapotekiya san agustin mixtepec || San Agustín Mixtepec Zapotec || Zapotec || zap || zapotekî || ziman |- | zpx || zapotekiya san baltazar loxiça || San Baltazar Loxicha Zapotec || Zapotec || zap || zapotekî || ziman |- | zab || zapotekiya san juan guelavia || San Juan Guelavía Zapotec || Zapotec || zap || zapotekî || ziman |- | ztu || zapotekiya san pablo güila || San Pablo Güilá Zapotec || Zapotec || zap || zapotekî || ziman |- | zpf || zapotekiya san pedro quiatoni || San Pedro Quiatoni Zapotec || Zapotec || zap || zapotekî || ziman |- | zpt || zapotekiya san vicente coatlan || San Vicente Coatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | ztn || zapotekiya santa catarina albarradas || Santa Catarina Albarradas Zapotec || Zapotec || zap || zapotekî || ziman |- | zpn || zapotekiya santa ines yatzeçi || Santa Inés Yatzechi Zapotec || Zapotec || zap || zapotekî || ziman |- | zpi || zapotekiya santa maria quiegolani || Santa María Quiegolani Zapotec || Zapotec || zap || zapotekî || ziman |- | zpr || zapotekiya santiago xanica || Santiago Xanica Zapotec || Zapotec || zap || zapotekî || ziman |- | zas || zapotekiya santo domingo albarradas || Santo Domingo Albarradas Zapotec || Zapotec || zap || zapotekî || ziman |- | zaa || zapotekiya sierra de juarez || Sierra de Juárez Zapotec || Zapotec || zap || zapotekî || ziman |- | zat || zapotekiya tabaa || Tabaa Zapotec || Zapotec || zap || zapotekî || ziman |- | ztt || zapotekiya tejalapan || Tejalapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zpz || zapotekiya texmelucan || Texmelucan Zapotec || Zapotec || zap || zapotekî || ziman |- | zts || zapotekiya tilquiapan || Tilquiapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zpk || zapotekiya tlacolulita || Tlacolulita Zapotec || Zapotec || zap || zapotekî || ziman |- | zph || zapotekiya totomaçapan || Totomachapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zax || zapotekiya xadani || Xadani Zapotec || Zapotec || zap || zapotekî || ziman |- | ztg || zapotekiya xanaguia || Xanaguía Zapotec || Zapotec || zap || zapotekî || ziman |- | zpu || zapotekiya yalalagî || Yalálag Zapotec || Zapotec || zap || zapotekî || ziman |- | zae || zapotekiya yareni || Yareni Zapotec || Zapotec || zap || zapotekî || ziman |- | zty || zapotekiya yatee || Yatee Zapotec || Zapotec || zap || zapotekî || ziman |- | zav || zapotekiya yatzaçî || Yatzachi Zapotec || Zapotec || zap || zapotekî || ziman |- | zpb || zapotekiya yautepec || Yautepec Zapotec || Zapotec || zap || zapotekî || ziman |- | ztx || zapotekiya zaaçila || Zaachila Zapotec || Zapotec || zap || zapotekî || ziman |- | zpw || zapotekiya zaniza || Zaniza Zapotec || Zapotec || zap || zapotekî || ziman |- | zpq || zapotekiya zoogoço || Zoogocho Zapotec || Zapotec || zap || zapotekî || ziman |- | zpv || zapotekiya çiçicapan || Chichicapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zpc || zapotekiya çoapan || Choapan Zapotec || Zapotec || zap || zapotekî || ziman |- | zam || zapotekoyiya mahuatlana navendî || Central Mahuatlán Zapotec || Zapotec || zap || zapotekî || ziman |- | zai || îsthmus zapotekî || Isthmus Zapotec || Zapotec || zap || zapotekî || ziman |- | cya || nopala çatinoyî || Nopala Chatino || Chatino || omq-cha || çatînoyî || malbat |- | omq-sjq || san Juan quiahije chatino || San Juan Quiahije Chatino || Chatino || omq-cha || çatînoyî || malbat |- | cta || tataltepec çatinoyî || Tataltepec Chatino || Chatino || omq-cha || çatînoyî || malbat |- | omq-teo || teojomulco çatinoyî || Teojomulco Chatino || Chatino || omq-cha || çatînoyî || malbat |- | ctz || zacatepec çatinoyî || Zacatepec Chatino || Chatino || omq-cha || çatînoyî || malbat |- | czn || zenzontepekiya çatîno || Zenzontepec Chatino || Chatino || omq-cha || çatînoyî || malbat |- | cly || çatinoyiya eastern highland || Eastern Highland Chatino || Chatino || omq-cha || çatînoyî || malbat |- | ctp || çatinoyiya western highland || Western Highland Chatino || Chatino || omq-cha || çatînoyî || malbat |- | zlw-ocs || çekiya kevn || Old Czech || Czech || cs || çekî || ziman |- | nan-dat || datian min || Datian Min || Chinese || zh || çînî || ziman |- | dng || dunganî || Dungan || Chinese || zh || çînî || ziman |- | bpn || dzao minî || Dzao Min || Chinese || zh || çînî || ziman |- | gan || ganî || Gan || Chinese || zh || çînî || ziman |- | nan-hlh || hailufeng min || Hailufeng Min || Chinese || zh || çînî || ziman |- | hak || hakkayî || Hakka || Chinese || zh || çînî || ziman |- | hnm || haynanîzî || Hainanese || Chinese || zh || çînî || ziman |- | nan-hbl || hokkien || Hokkien || Chinese || zh || çînî || ziman |- | czh || huizhouyî || Huizhou || Chinese || zh || çînî || ziman |- | cjy || jinî || Jin || Chinese || zh || çînî || ziman |- | yue || kantonî || Cantonese || Chinese || zh || çînî || ziman |- | mis-hkl || kelantan peranakan hokkien || Kelantan Peranakan Hokkien || Chinese || zh || çînî || ziman |- | luh || leizhou min || Leizhou Min || Chinese || zh || çînî || ziman |- | nan-lnx || longyan min || Longyan Min || Chinese || zh || çînî || ziman |- | cmn || mandarînî || Mandarin || Chinese || zh || çînî || ziman |- | mnp || min beî || Northern Min || Chinese || zh || çînî || ziman |- | cdo || min dongî || Eastern Min || Chinese || zh || çînî || ziman |- | czo || min zhongî || Central Min || Chinese || zh || çînî || ziman |- | nan || minnanî || Min Nan || Chinese || zh || çînî || ziman |- | csp || pinghuaya başûrî || Southern Pinghua || Chinese || zh || çînî || ziman |- | cpx || puxianî || Puxian Min || Chinese || zh || çînî || ziman |- | cnp || pînghuaya bakurî || Northern Pinghua || Chinese || zh || çînî || ziman |- | nan-zsh || sanxiang min || Sanxiang Min || Chinese || zh || çînî || ziman |- | sjc || shaojiang min || Shaojiang Min || Chinese || zh || çînî || ziman |- | zhx-sic || siçuanîzî || Sichuanese || Chinese || zh || çînî || ziman |- | zhx-tai || taişanîzî || Taishanese || Chinese || zh || çînî || ziman |- | mis-tnw || tangwang || Tangwang || Chinese || zh || çînî || ziman |- | nan-tws || teoçewî || Teochew || Chinese || zh || çînî || ziman |- | wxa || waxianghuayî || Waxiang || Chinese || zh || çînî || ziman |- | wuu || wuyî || Wu || Chinese || zh || çînî || ziman |- | hsn || xiangî || Xiang || Chinese || zh || çînî || ziman |- | nan-zhe || zhenan min || Zhenan Min || Chinese || zh || çînî || ziman |- | och || çîniya kevn || Old Chinese || Chinese || zh || çînî || ziman |- | lzh || çîniya klasîk || Classical Chinese || Chinese || zh || çînî || ziman |- | ltc || çîniya navîn || Middle Chinese || Chinese || zh || çînî || ziman |- | zhx-sht || şaozhou tuhuayî || Shaozhou Tuhua || Chinese || zh || çînî || ziman |- | smp || îbraniya samarî || Samaritan Hebrew || Hebrew || he || îbranî || ziman |- | ike || înuktitutiya kanadayî ya rojhilatî || Eastern Canadian Inuktitut || Inuktitut || iu || înuîtî || ziman |- | sga || îrlendiya kevn || Old Irish || Irish || ga || îrlendî || ziman |- | mga || îrlendiya navîn || Middle Irish || Irish || ga || îrlendî || ziman |- | pgl || îrlendiya prîmîtîv || Primitive Irish || Irish || ga || îrlendî || ziman |} == Nehatine wergerandin (490) == ''kom bê kod'' — koma en.wiktionary ne ziman e ne jî malbat; ''kod li ku tune'' — en.wiktionary kodek dide, lê li ku ne ziman e ne jî malbat; ''ziman li ku tune'' — ziman bi xwe li ku tune ye; ''heman ziman'' — en.wiktionary rêza zimanî di bin heman zimanî de bi alfabeyeke din dinivîse (wek Serbo-Croatian/Cyrillic). {| class="wikitable sortable" ! Kod !! Ziman !! en: ziman !! en: kom !! en: kod (kom) !! Çima |- | gvr || gurungiya rojavayî || Gurung || Gurung || gvr || heman ziman |- | man || mandingoyî || Mandingo || Mandingo || man || heman ziman |- | yrk || nenetsiya tundra || Nenets || Nenets || yrk || heman ziman |- | asc || asmatiya casuarina coast || Casuarina Coast Asmat || Asmat || ngf-asm || kod li ku tune |- | asy || yaosakor asmat || Yaosakor Asmat || Asmat || ngf-asm || kod li ku tune |- | cns || asmatiya navendî || Central Asmat || Asmat || ngf-asm || kod li ku tune |- | nks || asmatiya bakur || North Asmat || Asmat || ngf-asm || kod li ku tune |- | aws || awyuya başûrî || South Awyu || Awyu || ngf-awy || kod li ku tune |- | awu || awyuya navendî || Central Awyu || Awyu || ngf-awy || kod li ku tune |- | awv || awyuya jair || Jair Awyu || Awyu || ngf-awy || kod li ku tune |- | awy || awyuya edura || Edera Awyu || Awyu || ngf-awy || kod li ku tune |- | psa || asue awyuyî || Asue Awyu || Awyu || ngf-awy || kod li ku tune |- | yir || awyuyiya bakur || North Awyu || Awyu || ngf-awy || kod li ku tune |- | dna || daniya geliya grand a jorîn || Upper Grand Valley Dani || Dani || ngf-dan || kod li ku tune |- | dni || daniya geliya grand a jêrîn || Lower Grand Valley Dani || Dani || ngf-dan || kod li ku tune |- | dnt || daniya geliya grand a naverast || Mid Grand Valley Dani || Dani || ngf-dan || kod li ku tune |- | dnw || daniya rojavayî || Western Dani || Dani || ngf-dan || kod li ku tune |- | enf || forest enetsî || Forest Enets || Enets || syd-ene || kod li ku tune |- | enh || tundra enetsî || Tundra Enets || Enets || syd-ene || kod li ku tune |- | koi || komî-permyakî || Komi-Permyak || Komi || kv || kod li ku tune |- | kpv || komî-zîriyî || Komi-Zyrian || Komi || kv || kod li ku tune |- | urj-koo || komiya kevn || Old Komi || Komi || kv || kod li ku tune |- | urj-kya || komî-yazva || Komi-Yazva || Komi || kv || kod li ku tune |- | omw || tairorayiya başûr || South Tairora || Tairora || ngf-tai || kod li ku tune |- | tbg || tayrorayiya bakur || North Tairora || Tairora || ngf-tai || kod li ku tune |- | nlk || ninia yalî || Ninia Yali || Yali || ngf-yal || kod li ku tune |- | yac || pass valley yalî || Pass Valley Yali || Yali || ngf-yal || kod li ku tune |- | yli || angguruk yalî || Angguruk Yali || Yali || ngf-yal || kod li ku tune |- | abd || manîdeyî || Camarines Norte Agta || Agta || || kom bê kod |- | agk || îsarog agtayî || Isarog Agta || Agta || || kom bê kod |- | agt || agtayiya kagayanî ya navendî || Central Cagayan Agta || Agta || || kom bê kod |- | agv || remontado agta || Remontado Agta || Agta || || kom bê kod |- | agz || mount îrîga agta || Mount Iriga Agta || Agta || || kom bê kod |- | apf || pahanan agta || Pahanan Agta || Agta || || kom bê kod |- | atl || agtaya çiyaya iraya || Mount Iraya Agta || Agta || || kom bê kod |- | dgc || casiguran dumagat agtayî || Casiguran Dumagat Agta || Agta || || kom bê kod |- | due || umiray dumaget agtayî || Umiray Dumaget Agta || Agta || || kom bê kod |- | dul || alabat island agtayî || Alabat Island Agta || Agta || || kom bê kod |- | duo || dupaningan agtayî || Dupaningan Agta || Agta || || kom bê kod |- | duy || dicamay agtayî || Dicamay Agta || Agta || || kom bê kod |- | dyg || villa viciosa agtayî || || Agta || || kom bê kod |- | phi-din || dinapigue agtayî || Dinapigue Agta || Agta || || kom bê kod |- | phi-nag || nagtipunan agtayî || Nagtipunan Agta || Agta || || kom bê kod |- | agy || altaya başûrî || Southern Alta || Alta || || kom bê kod |- | aqn || altaya bakurî || Northern Alta || Alta || || kom bê kod |- | alt || altayiya başûrî || Southern Altai || Altai || || kom bê kod |- | atv || altayiya bakurî || Northern Altai || Altai || || kom bê kod |- | ams || amamî-oşîmayiya başûrî || Southern Amami Ōshima || Amami Ōshima || || kom bê kod |- | ryn || amamî-oşimayiya bakurî || Northern Amami Ōshima || Amami Ōshima || || kom bê kod |- | amu || amuzgoyî || Guerrero Amuzgo || Amuzgo || || kom bê kod |- | azg || san pedro amuzgos amuzgo || San Pedro Amuzgos Amuzgo || Amuzgo || || kom bê kod |- | azm || amuzgoyiya îpalapayî || Ipalapa Amuzgo || Amuzgo || || kom bê kod |- | aer || arrernteyiya rojavayî || Eastern Arrernte || Arrernte || || kom bê kod |- | are || arrernteya rojavayî || Western Arrernte || Arrernte || || kom bê kod |- | cjo || aşeninka pajonalî || Pajonal Ashéninka || Ashéninka || || kom bê kod |- | cpb || ucayali-yurúa aşeninkayî || Ucayali-Yurúa Ashéninka || Ashéninka || || kom bê kod |- | cpc || ajyininka apurucayaliyî || Apurucayali Ashéninka || Ashéninka || || kom bê kod |- | cpu || piçis aşeninkayî || Pichis Ashéninka || Ashéninka || || kom bê kod |- | cpy || south ucayali aşeninkayî || South Ucayali Ashéninka || Ashéninka || || kom bê kod |- | prq || aşeninka pereneyî || Perené Ashéninka || Ashéninka || || kom bê kod |- | atp || pudtol atta || Pudtol Atta || Atta || || kom bê kod |- | att || pamplona atta || Pamplona Atta || Atta || || kom bê kod |- | azt || faîre atta || Faire Atta || Atta || || kom bê kod |- | abc || ambala ayta || Ambala Ayta || Ayta || || kom bê kod |- | abp || abenlen ayta || Abenlen Ayta || Ayta || || kom bê kod |- | ays || sorsogon aytayî || Sorsogon Ayta || Ayta || || kom bê kod |- | ayt || bataan aytayî || Bataan Ayta || Ayta || || kom bê kod |- | ayy || tayabas aytayî || || Ayta || || kom bê kod |- | blx || mag-îndî ayta || Mag-Indi Ayta || Ayta || || kom bê kod |- | sgb || mag-ançî aytayî || Mag-Anchi Ayta || Ayta || || kom bê kod |- | bdl || bajwayiya endonezyayî || Indonesian Bajau || Bajau || || kom bê kod |- | bdr || bajwayiya kendava rojava || West Coast Bajau || Bajau || || kom bê kod |- | bfb || paurî barelî || Pauri Bareli || Bareli || || kom bê kod |- | bgd || rathwî barelî || Rathwi Bareli || Bareli || || kom bê kod |- | bpx || palya bareliyî || Palya Bareli || Bareli || || kom bê kod |- | nes || bhoti kinnaurî || Bhoti Kinnauri || Bhoti || || kom bê kod |- | sbu || stod bhotî || Stod Bhoti || Bhoti || || kom bê kod |- | spt || spîtî bhotî || Spiti Bhoti || Bhoti || || kom bê kod |- | bth || biatah bidayuhî || Biatah Bidayuh || Bidayuh || || kom bê kod |- | sdo || bukar-sadung bidayuhî || Bukar-Sadung Bidayuh || Bidayuh || || kom bê kod |- | sne || bau bidayuh || Bau Bidayuh || Bidayuh || || kom bê kod |- | bcl || bîkoliya naverast || Central Bikol || Bikol || || kom bê kod |- | bln || bîkolanoyiya başûr katanduanî || Southern Catanduanes Bikol || Bikol || || kom bê kod |- | bto || iriga bikolanoyî || Rinconada Bikol || Bikol || || kom bê kod |- | cts || bicolanoyiya northern catanduanes || Northern Catanduanes Bikol || Bikol || || kom bê kod |- | fbl || west albay bikolî || West Miraya Bikol || Bikol || || kom bê kod |- | lbl || libon bikolî || Libon Bikol || Bikol || || kom bê kod |- | rbl || miraya bikolî || East Miraya Bikol || Bikol || || kom bê kod |- | ubl || buhi'non bikolî || Buhi'non Bikol || Bikol || || kom bê kod |- | bfo || malba bîrîforî || Malba Birifor || Birifor || || kom bê kod |- | biv || biriforiya başûr || Southern Birifor || Birifor || || kom bê kod |- | bpr || koronadal blaanî || Koronadal Blaan || Blaan || || kom bê kod |- | bps || saranganî blaanî || Sarangani Blaan || Blaan || || kom bê kod |- | bnt-sbo || bomaya başûr || South Boma || Boma || || kom bê kod |- | boh || bomayî || North Boma || Boma || || kom bê kod |- | ebk || bontociya rojhilatî || Eastern Bontoc || Bontoc || || kom bê kod |- | lbk || bontociya navendî || Central Bontoc || Bontoc || || kom bê kod |- | obk || bontociya başûrî || Southern Bontoc || Bontoc || || kom bê kod |- | rbk || bontokiya bakurî || Northern Bontoc || Bontoc || || kom bê kod |- | vbk || bontokiya başûr-rojavayî || Southwestern Bontoc || Bontoc || || kom bê kod |- | boo || bozoyoya tiemacewe || Tiemacèwè Bozo || Bozo || || kom bê kod |- | boz || bozoyiya tieyaxo || Tiéyaxo Bozo || Bozo || || kom bê kod |- | bze || jenaama bozoyî || Jenaama Bozo || Bozo || || kom bê kod |- | bzx || bozoyiya hainyaxo || Hainyaxo Bozo || Bozo || || kom bê kod |- | bru || bruyiya rojhilatî || Eastern Bru || Bru || || kom bê kod |- | brv || bruyiya rojavayî || Western Bru || Bru || || kom bê kod |- | bzh || mapos buangî || Mapos Buang || Buang || || kom bê kod |- | mmo || mangga buangî || Mangga Buang || Buang || || kom bê kod |- | buh || younuo bunuyî || Younuo Bunu || Bunu || || kom bê kod |- | bwn || wunay bunuyî || Wunai Bunu || Bunu || || kom bê kod |- | bwx || bu-nao bunuyî || Bu-Nao Bunu || Bunu || || kom bê kod |- | pnu || jiongnai bunuyî || Jiongnai Bunu || Bunu || || kom bê kod |- | cja || çamiya rojavayî || Western Cham || Cham || || kom bê kod |- | cjm || çamiya rojhilatî || Eastern Cham || Cham || || kom bê kod |- | bgr || bawm çînî || Bawm Chin || Chin || || kom bê kod |- | cbl || bualkhaw çinî || Bualkhaw Chin || Chin || || kom bê kod |- | cfm || falam çinî || Falam Chin || Chin || || kom bê kod |- | ckn || kaang çinî || Kaang Chin || Chin || || kom bê kod |- | clj || laitu çinî || Laitu Chin || Chin || || kom bê kod |- | cmr || mro çinî || Mro Chin || Chin || || kom bê kod |- | cnb || çinbon çinî || Chinbon Chin || Chin || || kom bê kod |- | cnk || khumi çinî || Khumi Chin || Chin || || kom bê kod |- | cnw || ngawn çinî || Ngawn Chin || Chin || || kom bê kod |- | csh || aşo çinî || Asho Chin || Chin || || kom bê kod |- | csj || songlai çinî || Songlai Chin || Chin || || kom bê kod |- | csv || sumtu çinî || Sumtu Chin || Chin || || kom bê kod |- | csy || siyin çinî || Siyin Chin || Chin || || kom bê kod |- | ctd || tedim çinî || Tedim Chin || Chin || || kom bê kod |- | cth || thaiphum çinî || Thaiphum Chin || Chin || || kom bê kod |- | czt || zotung çinî || Zotung Chin || Chin || || kom bê kod |- | dao || daai çinî || Daai Chin || Chin || || kom bê kod |- | mrh || mara çinî || Mara Chin || Chin || || kom bê kod |- | mwq || mün çinî || Mün Chin || Chin || || kom bê kod |- | sez || senthang çinî || Senthang Chin || Chin || || kom bê kod |- | tcp || tawr çinî || Tawr Chin || Chin || || kom bê kod |- | tcz || thado çinî || Thado Chin || Chin || || kom bê kod |- | cco || comaltepec çinantecî || Comaltepec Chinantec || Chinantec || || kom bê kod |- | chj || ojitlan çinantecî || Ojitlán Chinantec || Chinantec || || kom bê kod |- | chq || quiotepec çinantecî || Quiotepec Chinantec || Chinantec || || kom bê kod |- | chz || ozumacin çinantecî || Ozumacín Chinantec || Chinantec || || kom bê kod |- | cle || lealao çinantecî || Lealao Chinantec || Chinantec || || kom bê kod |- | cnl || lalana çinantecî || Lalana Chinantec || Chinantec || || kom bê kod |- | cnt || tepetotutla çinantecî || Tepetotutla Chinantec || Chinantec || || kom bê kod |- | cpa || palantla çinantecî || Palantla Chinantec || Chinantec || || kom bê kod |- | csa || çiltepec çinantecî || Chiltepec Chinantec || Chinantec || || kom bê kod |- | cso || soçiapam çinantecî || Sochiapam Chinantec || Chinantec || || kom bê kod |- | cte || tepinapa çinantecî || Tepinapa Chinantec || Chinantec || || kom bê kod |- | ctl || tlacoatzintepec çinantecî || Tlacoatzintepec Chinantec || Chinantec || || kom bê kod |- | cuc || usila çinantecî || Usila Chinantec || Chinantec || || kom bê kod |- | cvn || valle nacional çinantecî || Valle Nacional Chinantec || Chinantec || || kom bê kod |- | chd || highland oaxaca çontalî || Highland Oaxaca Chontal || Chontal || || kom bê kod |- | clo || lowland oaxaca çontalî || Lowland Oaxaca Chontal || Chontal || || kom bê kod |- | crq || iyo'wujwa çoroteyî || Iyo'wujwa Chorote || Chorote || || kom bê kod |- | crt || iyojwa'ja çoroteyî || Iyojwa'ja Chorote || Chorote || || kom bê kod |- | dtp || dusuniya navendî || Central Dusun || Dusun || || kom bê kod |- | kzs || sugut dusunî || Sugut Dusun || Dusun || || kom bê kod |- | kzt || tambunan dusunî || || Dusun || || kom bê kod |- | tdu || tempasuk dusunî || || Dusun || || kom bê kod |- | erk || efateyiya başûr || South Efate || Efate || || kom bê kod |- | llp || efateyiya bakur || North Efate || Efate || || kom bê kod |- | gas || adiwasi garasiayî || Adiwasi Garasia || Garasia || || kom bê kod |- | gra || rajput garasiayî || Rajput Garasia || Garasia || || kom bê kod |- | ghe || ghaleyiya başûrî || Southern Ghale || Ghale || || kom bê kod |- | ghh || ghaleyî bakurî || Northern Ghale || Ghale || || kom bê kod |- | ght || kutang ghaleyî || Kutang Ghale || Ghale || || kom bê kod |- | gis || gizigayiya bakur || North Giziga || Giziga || || kom bê kod |- | giz || gizigayiya başûr || South Giziga || Giziga || || kom bê kod |- | hnd || hindkoyiya başûrî || Southern Hindko || Hindko || || kom bê kod |- | hno || hindkoyiya bakurî || Northern Hindko || Hindko || || kom bê kod |- | cqd || çuanqiandian cluster miaoyî || Chuanqiandian Cluster Miao || Hmong || || kom bê kod |- | hea || qiandong miaoyiya bakurî || Northern Qiandong Miao || Hmong || || kom bê kod |- | hma || maşan hmongiya başûrî || Southern Mashan Hmong || Hmong || || kom bê kod |- | hmc || huişui hmongiya navendî || Central Huishui Hmong || Hmong || || kom bê kod |- | hmd || a-hmaoyî || A-Hmao || Hmong || || kom bê kod |- | hme || huişui hmongiya rojhilatî || Eastern Huishui Hmong || Hmong || || kom bê kod |- | hmf || hmong donî || Hmong Don || Hmong || || kom bê kod |- | hmg || guiyang hmongiya başûr-rojavayî || Southwestern Guiyang Hmong || Hmong || || kom bê kod |- | hmh || huişui hmongiya başûr-rojavayî || Southwestern Huishui Hmong || Hmong || || kom bê kod |- | hmi || huişui hmongiya bakurî || Northern Huishui Hmong || Hmong || || kom bê kod |- | hmj || geyî || Ge || Hmong || || kom bê kod |- | hml || luopohe hmongî || Luopohe Hmong || Hmong || || kom bê kod |- | hmm || maşan hmongiya navendî || Central Mashan Hmong || Hmong || || kom bê kod |- | hmp || maşan hmongiya bakurî || Northern Mashan Hmong || Hmong || || kom bê kod |- | hmq || qiandong miaoyiya rojhilatî || Eastern Qiandong Miao || Hmong || || kom bê kod |- | hms || qiandong miaoyiya başûrî || Southern Qiandong Miao || Hmong || || kom bê kod |- | hmv || hmong dôî || Hmong Dô || Hmong || || kom bê kod |- | hmw || maşan hmongiya rojavayî || Western Mashan Hmong || Hmong || || kom bê kod |- | hmy || guiyang hmongiya başûrî || Southern Guiyang Hmong || Hmong || || kom bê kod |- | hmz || hmong şuayî || Hmong Shua || Hmong || || kom bê kod |- | hnj || hmongiya hêşîn || Green Hmong || Hmong || || kom bê kod |- | hrm || horned miaoyî || Horned Miao || Hmong || || kom bê kod |- | huj || guiyang hmongiya bakurî || Northern Guiyang Hmong || Hmong || || kom bê kod |- | mmr || xiangxi miaoyiya rojavayî || Western Xiangxi Miao || Hmong || || kom bê kod |- | muq || xiangxi miaoyiya rojhilatî || Eastern Xiangxi Miao || Hmong || || kom bê kod |- | mww || hmongî || White Hmong || Hmong || || kom bê kod |- | sfm || small flowery miaoyî || Small Flowery Miao || Hmong || || kom bê kod |- | hto || minica huitotoyî || Minica Huitoto || Huitoto || || kom bê kod |- | huu || murui huitotoyî || Murui Huitoto || Huitoto || || kom bê kod |- | hux || nüpode huitotoyî || Nüpode Huitoto || Huitoto || || kom bê kod |- | ifa || amganad ifugaoyî || Amganad Ifugao || Ifugao || || kom bê kod |- | ifb || batad ifugaoyî || Batad Ifugao || Ifugao || || kom bê kod |- | ifk || tuwali ifugaoyî || Tuwali Ifugao || Ifugao || || kom bê kod |- | ifu || mayoyao ifugaoyî || Mayoyao Ifugao || Ifugao || || kom bê kod |- | bjx || banao îtneg || Banao Itneg || Itneg || || kom bê kod |- | itb || binongan itnegî || Binongan Itneg || Itneg || || kom bê kod |- | iti || înlaod itnegî || Inlaod Itneg || Itneg || || kom bê kod |- | itt || maeng itnegî || Maeng Itneg || Itneg || || kom bê kod |- | ity || moyadan itnegî || Moyadan Itneg || Itneg || || kom bê kod |- | tis || masadiit itnegî || Masadiit Itneg || Itneg || || kom bê kod |- | dtb || labuk-kinabatangan kadazanî || Labuk-Kinabatangan Kadazan || Kadazan || || kom bê kod |- | kqt || kadazaniya çemê kliasê || Klias River Kadazan || Kadazan || || kom bê kod |- | kzj || kadazaniya beravî || Coastal Kadazan || Kadazan || || kom bê kod |- | kzf || da'a kailiyî || Da'a Kaili || Kaili || || kom bê kod |- | lew || ledo kailiyî || Ledo Kaili || Kaili || || kom bê kod |- | unz || unde kailî || Unde Kaili || Kaili || || kom bê kod |- | kkg || mabaka valley kalingayî || Mabaka Valley Kalinga || Kalinga || || kom bê kod |- | kmd || madukayang kalingayî || Madukayang Kalinga || Kalinga || || kom bê kod |- | kmk || limos kalingayî || Limos Kalinga || Kalinga || || kom bê kod |- | kml || tanudan kalingayî || Tanudan Kalinga || Kalinga || || kom bê kod |- | knb || lubuagan kalingayî || Lubuagan Kalinga || Kalinga || || kom bê kod |- | ksc || kalingayiya başûrî || Southern Kalinga || Kalinga || || kom bê kod |- | kyb || butbut kalingayî || Butbut Kalinga || Kalinga || || kom bê kod |- | ify || keley-i kallahanî || Keley-I Kallahan || Kallahan || || kom bê kod |- | kak || kayapa kallahanî || Kayapa Kallahan || Kallahan || || kom bê kod |- | tne || tinoc kallahanî || Tinoc Kallahan || Kallahan || || kom bê kod |- | ktv || katuyiya rojhilatî || Eastern Katu || Katu || || kom bê kod |- | kuf || katuyiya rojavayî || Western Katu || Katu || || kom bê kod |- | eky || kayahiya rojhilatî || Eastern Kayah || Kayah || || kom bê kod |- | kyu || kayahiya rojavayî || Western Kayah || Kayah || || kom bê kod |- | kee || keresiya rojhilatî || Eastern Keres || Keres || || kom bê kod |- | kjq || keresiya rojavayî || Western Keres || Keres || || kom bê kod |- | kiw || kiwaiyiya bakur-rojhilat || Northeast Kiwai || Kiwai || || kom bê kod |- | kjd || kiwaiyiya başûrî || Southern Kiwai || Kiwai || || kom bê kod |- | kbk || grass koiariyî || Grass Koiari || Koiari || || kom bê kod |- | kpx || koiariyiya çiyayî || Mountain Koiari || Koiari || || kom bê kod |- | gjk || kaçi koliyî || Kachi Koli || Koli || || kom bê kod |- | kvx || parkari koliyî || Parkari Koli || Koli || || kom bê kod |- | kxp || wadiyara koliyî || Wadiyara Koli || Koli || || kom bê kod |- | kqo || krahniya rojhilatî || Eastern Krahn || Krahn || || kom bê kod |- | krw || krahniya rojavayî || Western Krahn || Krahn || || kom bê kod |- | ktj || plapo krumenî || Plapo Krumen || Krumen || || kom bê kod |- | pye || pye krumenî || Pye Krumen || Krumen || || kom bê kod |- | ted || tepo krumenî || Tepo Krumen || Krumen || || kom bê kod |- | lcp || lawayiya rojavayî || Western Lawa || Lawa || || kom bê kod |- | lwl || lawayiya rojhilatî || Eastern Lawa || Lawa || || kom bê kod |- | len || lencayiya honduranî || Honduran Lenca || Lenca || || kom bê kod |- | nai-sln || salvadoran lencayî || Salvadoran Lenca || Lenca || || kom bê kod |- | low || tampias lobuyî || Tampias Lobu || Lobu || || kom bê kod |- | ruu || lanas lobuyî || Lanas Lobu || Lobu || || kom bê kod |- | lbr || lorungiya bakurî || Northern Lorung || Lorung || || kom bê kod |- | lrr || lorungiya başûrî || Southern Lorung || Lorung || || kom bê kod |- | lrc || luriya bakurî || Northern Luri || Luri || || kom bê kod |- | luz || luriya başûrî || Southern Luri || Luri || || kom bê kod |- | mgp || magariya rojhilatî || Eastern Magar || Magar || || kom bê kod |- | mrd || magariya rojavayî || Western Magar || Magar || || kom bê kod |- | mjd || maiduyiya bakur-rojava || Northwest Maidu || Maidu || || kom bê kod |- | nmu || maiduyiya bakur-rojhilat || Northeast Maidu || Maidu || || kom bê kod |- | nsz || nisenanî || Nisenan || Maidu || || kom bê kod |- | vmv || maiduyiya geliyî || Valley Maidu || Maidu || || kom bê kod |- | mcu || mambilayiya kamerûnê || Cameroon Mambila || Mambila || || kom bê kod |- | mzk || mambilayiya nîjeryayî || Nigeria Mambila || Mambila || || kom bê kod |- | emk || maninkakaniya rojhilatî || Eastern Maninkakan || Maninkakan || || kom bê kod |- | mlq || malinkî || Western Maninkakan || Maninkakan || || kom bê kod |- | mwk || kita maninkakanî || Kita Maninkakan || Maninkakan || || kom bê kod |- | mqm || marquesana başûr || South Marquesan || Marquesan || || kom bê kod |- | mrq || marquesana bakur || North Marquesan || Marquesan || || kom bê kod |- | mss || maselayiya rojava || West Masela || Masela || || kom bê kod |- | mxz || maselayiya navendî || Central Masela || Masela || || kom bê kod |- | vme || maselayiya rojhilat || East Masela || Masela || || kom bê kod |- | maz || mazahuayiya navendî || Central Mazahua || Mazahua || || kom bê kod |- | mmc || miçoacan mazahuayî || Michoacán Mazahua || Mazahua || || kom bê kod |- | tpc || azoyú me'phaayî || Azoyú Me'phaa || Me'phaa || || kom bê kod |- | tpl || tlacoapa me'phaayî || Tlacoapa Me'phaa || Me'phaa || || kom bê kod |- | tpx || akatepekiya me'phaayî || Acatepec Me'phaa || Me'phaa || || kom bê kod |- | dro || daro-matu melanauyî || Daro-Matu Melanau || Melanau || || kom bê kod |- | mel || melanauyiya navendî || Central Melanau || Melanau || || kom bê kod |- | sdx || sibu melanauyî || Sibu Melanau || Melanau || || kom bê kod |- | emg || meohangiya rojhilatî || Eastern Meohang || Meohang || || kom bê kod |- | raf || meohangiya rojavayî || Western Meohang || Meohang || || kom bê kod |- | csi || coast miwokî || Coast Miwok || Miwok || || kom bê kod |- | csm || sierra mîwokiya navendî || Central Sierra Miwok || Miwok || || kom bê kod |- | lmw || miwokiya golî || Lake Miwok || Miwok || || kom bê kod |- | mkq || bay miwokî || Bay Miwok || Miwok || || kom bê kod |- | nsq || miwokiya sierraya bakurî || Northern Sierra Miwok || Miwok || || kom bê kod |- | pmw || plains miwokî || Plains Miwok || Miwok || || kom bê kod |- | skd || sierra miwokiya başûrî || Southern Sierra Miwok || Miwok || || kom bê kod |- | mco || coatlan mixeyî || Coatlán Mixe || Mixe || || kom bê kod |- | mir || isthmus mixeyî || Isthmus Mixe || Mixe || || kom bê kod |- | mto || totontepec mixeyî || Totontepec Mixe || Mixe || || kom bê kod |- | mxp || tlahuitoltepec mixeyî || Tlahuitoltepec Mixe || Mixe || || kom bê kod |- | mxq || juquila mixeyî || Juquila Mixe || Mixe || || kom bê kod |- | mzl || mazatlan mixeyî || Mazatlán Mixe || Mixe || || kom bê kod |- | neq || mixeyiya navendî ya bakur || North Central Mixe || Mixe || || kom bê kod |- | pxm || mixeyiya quetzaltepec || Quetzaltepec Mixe || Mixe || || kom bê kod |- | cmo || mnongiya navendî || Central Mnong || Mnong || || kom bê kod |- | mng || mnongiya rojhilatî || Eastern Mnong || Mnong || || kom bê kod |- | mnn || mnongiya başûrî || Southern Mnong || Mnong || || kom bê kod |- | kxi || keningau murutî || Keningau Murut || Murut || || kom bê kod |- | mvv || tagal murutî || Tagal Murut || Murut || || kom bê kod |- | sbr || sembakung murutî || Sembakung Murut || Murut || || kom bê kod |- | slg || selungaî murutî || Selungai Murut || Murut || || kom bê kod |- | srk || serudung murutî || Serudung Murut || Murut || || kom bê kod |- | tih || timugon murutî || Timugon Murut || Murut || || kom bê kod |- | kti || muyuyiya bakur || North Muyu || Muyu || || kom bê kod |- | kts || muyuyiya başûr || South Muyu || Muyu || || kom bê kod |- | jmn || makuri nagayî || Makuri Naga || Naga || || kom bê kod |- | kfw || kharam nagayî || Kharam Naga || Naga || || kom bê kod |- | kix || khiamniungan nagayî || Khiamniungan Naga || Naga || || kom bê kod |- | lpn || long phuri nagayî || Long Phuri Naga || Naga || || kom bê kod |- | lzn || leinong nagayî || Leinong Naga || Naga || || kom bê kod |- | nbe || konyak nagayî || Konyak Naga || Naga || || kom bê kod |- | nbi || mao nagayî || Mao Naga || Naga || || kom bê kod |- | nbu || rongmei nagayî || Rongmei Naga || Naga || || kom bê kod |- | nct || çothe nagayî || Chothe Naga || Naga || || kom bê kod |- | njh || lotha nagayî || Lotha Naga || Naga || || kom bê kod |- | njn || liangmai nagayî || Liangmai Naga || Naga || || kom bê kod |- | nkb || khoibu nagayî || Khoibu Naga || Naga || || kom bê kod |- | nkf || inpui nagayî || Inpui Naga || Naga || || kom bê kod |- | nkh || khezha nagayî || Khezha Naga || Naga || || kom bê kod |- | nki || thangal nagayî || Thangal Naga || Naga || || kom bê kod |- | nlq || lao nagayî || Lao Naga || Naga || || kom bê kod |- | nma || maram nagayî || Maram Naga || Naga || || kom bê kod |- | nme || mzieme nagayî || Mzieme Naga || Naga || || kom bê kod |- | nmf || tangkhul nagayî || Tangkhul Naga || Naga || || kom bê kod |- | nmh || monsang nagayî || Monsang Naga || Naga || || kom bê kod |- | nmo || moyon nagayî || Moyon Naga || Naga || || kom bê kod |- | nng || maring nagayî || Maring Naga || Naga || || kom bê kod |- | nnl || nagayiya rengmaya bakur || Northern Rengma Naga || Naga || || kom bê kod |- | npo || poçuri nagayî || Pochuri Naga || Naga || || kom bê kod |- | npu || puimei nagayî || Puimei Naga || Naga || || kom bê kod |- | nqq || kyan-karyaw nagayî || Kyan-Karyaw Naga || Naga || || kom bê kod |- | nre || rengma nagayiya başûr || Southern Rengma Naga || Naga || || kom bê kod |- | nri || çokri nagayî || Chokri Naga || Naga || || kom bê kod |- | nsa || sangtam nagayî || Sangtam Naga || Naga || || kom bê kod |- | nzm || zeme nagayî || Zeme Naga || Naga || || kom bê kod |- | pmx || poumei nagayî || Poumei Naga || Naga || || kom bê kod |- | pzn || para nagayî || Para Naga || Naga || || kom bê kod |- | tvt || tutsa nagayî || Tutsa Naga || Naga || || kom bê kod |- | umn || makyan nagayî || Makyan Naga || Naga || || kom bê kod |- | yim || yimçungru nagayî || Yimchungru Naga || Naga || || kom bê kod |- | nd || ndebeliya bakurî || Northern Ndebele || Ndebele || || kom bê kod |- | nr || ndebeliya başûrî || Southern Ndebele || Ndebele || || kom bê kod |- | nni || nuauluyiya bakur || North Nuaulu || Nuaulu || || kom bê kod |- | nxl || nuauluyiya başûr || South Nuaulu || Nuaulu || || kom bê kod |- | nnw || nuniya başûrî || Southern Nuni || Nuni || || kom bê kod |- | nuv || nuniya bakurî || Northern Nuni || Nuni || || kom bê kod |- | css || ohloniya başûr || Southern Ohlone || Ohlone || || kom bê kod |- | cst || ohloniya bakur || Northern Ohlone || Ohlone || || kom bê kod |- | ojb || ojibweyiya bakurî || Northwestern Ojibwa || Ojibwa || || kom bê kod |- | ojc || ojibweyiya navendî || Central Ojibwa || Ojibwa || || kom bê kod |- | ojg || ojibweyiya rojhilatî || Eastern Ojibwa || Ojibwa || || kom bê kod |- | ojs || ojibweyiya severnê || Severn Ojibwa || Ojibwa || || kom bê kod |- | ojw || ojibweyiya rojavayî || Western Ojibwa || Ojibwa || || kom bê kod |- | aun || molmo one || Molmo One || One || || kom bê kod |- | oin || oneyiya inebu || Inebu One || One || || kom bê kod |- | okk || oneyiya kwamtim || Kwamtim One || One || || kom bê kod |- | onk || oneyiya kabore || Kabore One || One || || kom bê kod |- | onr || oneyiya bakurî || Northern One || One || || kom bê kod |- | osu || oneyiya başûrî || Southern One || One || || kom bê kod |- | kmj || kumarbhag pahariayî || Kumarbhag Paharia || Paharia || || kom bê kod |- | mjt || sawriya pahariayî || Sawriya Paharia || Paharia || || kom bê kod |- | pce || ruçing palaungî || Ruching Palaung || Palaung || || kom bê kod |- | pll || şwe palaungî || Shwe Palaung || Palaung || || kom bê kod |- | rbb || rumaî palaungî || Rumai Palaung || Palaung || || kom bê kod |- | plc || palawanoyiya navendî || Central Palawano || Palawano || || kom bê kod |- | plv || palawanoyiya başûr-rojava || Southwest Palawano || Palawano || || kom bê kod |- | plw || palawanoyî || Brooke's Point Palawano || Palawano || || kom bê kod |- | pbs || pamiya navendî || Central Pame || Pame || || kom bê kod |- | pmq || pameyiya bakurî || Northern Pame || Pame || || kom bê kod |- | pmz || pameyiya başûrî || Southern Pame || Pame || || kom bê kod |- | pez || penaniya rojhilatî || Eastern Penan || Penan || || kom bê kod |- | pne || penaniya rojavayî || Western Penan || Penan || || kom bê kod |- | kju || kaşayayî || Kashaya || Pomo || || kom bê kod |- | peb || pomoyiya rojhilatî || Eastern Pomo || Pomo || || kom bê kod |- | pef || pomoyiya bakur-rojhilatî || Northeastern Pomo || Pomo || || kom bê kod |- | pej || pomoyiya bakurî || Northern Pomo || Pomo || || kom bê kod |- | peq || pomoyiya başûrî || Southern Pomo || Pomo || || kom bê kod |- | pom || pomoyiya başûr-rojhilatî || Southeastern Pomo || Pomo || || kom bê kod |- | poo || pomoyiya navendî || Central Pomo || Pomo || || kom bê kod |- | pbe || popolocayiya mezontla || Mezontla Popoloca || Popoloca || || kom bê kod |- | pbf || popolocayiya coyotepec || Coyotepec Popoloca || Popoloca || || kom bê kod |- | pca || popolocayiya santa ines ahuatempan || Santa Inés Ahuatempan Popoloca || Popoloca || || kom bê kod |- | pls || popolocayiya san marcos tlalcoyalco || San Marcos Tlalcoyalco Popoloca || Popoloca || || kom bê kod |- | poe || popolocayiya san juan atzingo || San Juan Atzingo Popoloca || Popoloca || || kom bê kod |- | pow || popolocayiya san felipe otlaltepec || San Felipe Otlaltepec Popoloca || Popoloca || || kom bê kod |- | pps || popolocayiya san luis temalacayuca || San Luís Temalacayuca Popoloca || Popoloca || || kom bê kod |- | plo || popolucayiya oluta || Oluta Popoluca || Popoluca || || kom bê kod |- | poi || popolucayiya highland || Highland Popoluca || Popoluca || || kom bê kod |- | poq || popolucayiya texistepec || Texistepec Popoluca || Popoluca || || kom bê kod |- | pos || popolucayiya sayula || Sayula Popoluca || Popoluca || || kom bê kod |- | pmi || pumiya bakurî || Northern Pumi || Pumi || || kom bê kod |- | pmj || pumiya başûrî || Southern Pumi || Pumi || || kom bê kod |- | kjp || pwoyiya rojhilatî || Eastern Pwo || Pwo || || kom bê kod |- | kjt || phrae pwoyî || Phrae Pwo || Pwo || || kom bê kod |- | pwo || pwoyiya rojavayî || Western Pwo || Pwo || || kom bê kod |- | pww || pwoyiya bakurî || Northern Pwo || Pwo || || kom bê kod |- | cng || qiangiya bakurî || Northern Qiang || Qiang || || kom bê kod |- | qxs || kiangiya başûrî || Southern Qiang || Qiang || || kom bê kod |- | rgs || roglayiya başûrî || Southern Roglai || Roglai || || kom bê kod |- | roc || cacgia roglaî || Cacgia Roglai || Roglai || || kom bê kod |- | rog || roglayiya bakurî || Northern Roglai || Roglai || || kom bê kod |- | hmb || humburi senniyî || Humburi Senni || Senni || || kom bê kod |- | ses || sonxayî || Koyraboro Senni || Senni || || kom bê kod |- | dyi || djiminiyî || Djimini || Senoufo || || kom bê kod |- | myk || mamara senoufoyî || Mamara || Senoufo || || kom bê kod |- | nfr || nafaanrayî || Nafaanra || Senoufo || || kom bê kod |- | plr || palaka senoufoyî || Palaka || Senoufo || || kom bê kod |- | sef || cebaara senoufoyî || Cebaara || Senoufo || || kom bê kod |- | sen || nanerige senoufoyî || Nanerige || Senoufo || || kom bê kod |- | sep || sìcìte senoufoyî || Sicite || Senoufo || || kom bê kod |- | seq || senara senoufoyî || Senara || Senoufo || || kom bê kod |- | sev || nyarafolo senoufoyî || Nyarafolo || Senoufo || || kom bê kod |- | shz || syenara senoufoyî || Syenara || Senoufo || || kom bê kod |- | spp || supyireyî || Supyire || Senoufo || || kom bê kod |- | tgw || tagwana senoufoyî || Tagwana || Senoufo || || kom bê kod |- | bks || masbate sorsogon || Masbate Sorsogon || Sorsogon || || kom bê kod |- | srv || waray sorsogonî || Waray Sorsogon || Sorsogon || || kom bê kod |- | sti || bulo stiengî || Bulo Stieng || Stieng || || kom bê kod |- | stt || budeh stiengî || Budeh Stieng || Stieng || || kom bê kod |- | laa || lapuyan subanunî || Lapuyan Subanun || Subanen || || kom bê kod |- | sfe || subanuna rojhilatî || Eastern Subanun || Subanen || || kom bê kod |- | skn || kolibugan subanonî || Kolibugan Subanon || Subanen || || kom bê kod |- | stb || subaneniya bakurî || Northern Subanen || Subanen || || kom bê kod |- | suc || subanoniya rojavayî || Western Subanon || Subanen || || kom bê kod |- | syb || subaneniya navendî || Central Subanen || Subanen || || kom bê kod |- | taj || tamangiya rojhilatî || Eastern Tamang || Tamang || || kom bê kod |- | tdg || tamangiya rojavayî || Western Tamang || Tamang || || kom bê kod |- | tge || tamangiya gorkhaya rojhilatî || Eastern Gorkha Tamang || Tamang || || kom bê kod |- | tmk || tamangiya bakur-rojavayî || || Tamang || || kom bê kod |- | tsf || tamangiya başûr-rojavayî || || Tamang || || kom bê kod |- | taa || tananaya jêrîn || Lower Tanana || Tanana || || kom bê kod |- | tau || tananaya jorîn || Upper Tanana || Tanana || || kom bê kod |- | tac || tarahumarayiya lowland || Lowland Tarahumara || Tarahumara || || kom bê kod |- | tar || tarahumarayiya navendî || Central Tarahumara || Tarahumara || || kom bê kod |- | tcu || tarahumarayiya başûr-rojhilatî || Southeastern Tarahumara || Tarahumara || || kom bê kod |- | thh || tarahumarayiya bakurî || Northern Tarahumara || Tarahumara || || kom bê kod |- | twr || tarahumarayiya başûr-rojavayî || Southwestern Tarahumara || Tarahumara || || kom bê kod |- | tre || taranganiya rojhilat || East Tarangan || Tarangan || || kom bê kod |- | txn || taranganiya rojava || West Tarangan || Tarangan || || kom bê kod |- | bnj || tawbuîdiya rojhilatî || Eastern Tawbuid || Tawbuid || || kom bê kod |- | twb || tawbuidiya rojavayî || Western Tawbuid || Tawbuid || || kom bê kod |- | tee || huehuetla tepehuayî || Huehuetla Tepehua || Tepehua || || kom bê kod |- | tpp || pisaflores tepehuayî || Pisaflores Tepehua || Tepehua || || kom bê kod |- | tpt || tlaçiçilco tepehuayî || Tlachichilco Tepehua || Tepehua || || kom bê kod |- | ntp || tepehuaniya bakurî || Northern Tepehuan || Tepehuan || || kom bê kod |- | stp || tepehuaniya başûr-rojhilat || Southeastern Tepehuan || Tepehuan || || kom bê kod |- | tla || tepehuaniya başûr-rojavayî || Southwestern Tepehuan || Tepehuan || || kom bê kod |- | itd || tidongiya başûrî || Southern Tidung || Tidung || || kom bê kod |- | ntd || tidongiya bakurî || Northern Tidung || Tidung || || kom bê kod |- | tcw || totonaciya tekpatlan || Tecpatlán Totonac || Totonac || || kom bê kod |- | tku || totonaciya nekaksaya jorîn || Upper Necaxa Totonac || Totonac || || kom bê kod |- | tlc || totonakiya misantla || Misantla Totonac || Totonac || || kom bê kod |- | tlp || totonakiya filomena mata-coahuitlan || Filomena Mata-Coahuitlán Totonac || Totonac || || kom bê kod |- | toc || totonakiya coyutla || Coyutla Totonac || Totonac || || kom bê kod |- | too || totonakiya xicotepec de juarez || Xicotepec de Juárez Totonac || Totonac || || kom bê kod |- | top || totonakiya papantla || Papantla Totonac || Totonac || || kom bê kod |- | tos || totonakiya çiyayî || Highland Totonac || Totonac || || kom bê kod |- | tqt || totonaciya rojavayî || Western Totonac || Totonac || || kom bê kod |- | tsp || toussianiya bakurî || Northern Toussian || Toussian || || kom bê kod |- | wib || toussianiya başûrî || Southern Toussian || Toussian || || kom bê kod |- | bhq || tukang besî başûr || South Tukang Besi || Tukang Besi || || kom bê kod |- | khc || tukang besiya bakur || North Tukang Besi || Tukang Besi || || kom bê kod |- | tbn || barro negro tuneboyî || Barro Negro Tunebo || Tunebo || || kom bê kod |- | tnb || tuneboyiya rojavayî || Western Tunebo || Tunebo || || kom bê kod |- | tnd || angosturas tuneboyî || Angosturas Tunebo || Tunebo || || kom bê kod |- | tuf || tuneboyiya navendî || Central Tunebo || Tunebo || || kom bê kod |- | tce || tutçoneyiya başûrî || Southern Tutchone || Tutchone || || kom bê kod |- | ttm || tutçoneyiya bakurî || Northern Tutchone || Tutchone || || kom bê kod |- | mcy || watutiya başûr || South Watut || Watut || || kom bê kod |- | mpl || watutiya navîn || Middle Watut || Watut || || kom bê kod |- | una || watutiya bakur || North Watut || Watut || || kom bê kod |- | zoc || zoqueyiya copainala || Copainalá Zoque || Zoque || || kom bê kod |- | zoh || zoqueyiya çimalapa || Chimalapa Zoque || Zoque || || kom bê kod |- | zoq || zoqueyiya tabasco || Tabasco Zoque || Zoque || || kom bê kod |- | zor || zoqueyiya rayón || Rayón Zoque || Zoque || || kom bê kod |- | zos || zoqueyiya francisco león || Francisco León Zoque || Zoque || || kom bê kod |- | ckb || soranî || Central Kurdish || Kurdish || ku || zaravayên kurdî di beşa "Bi zaravayên din" de ne |- | lki || lekî || Laki || Kurdish || ku || zaravayên kurdî di beşa "Bi zaravayên din" de ne |- | sdh || kurdiya başûrî || Southern Kurdish || Kurdish || ku || zaravayên kurdî di beşa "Bi zaravayên din" de ne |- | aae || || Arbëresh Albanian || Albanian/Arbëresh || sq || ziman li ku tune |- | aat || || Arvanitika Albanian || Albanian/Arvanitika || sq || ziman li ku tune |- | aln || || Gheg Albanian || Albanian/Gheg || sq || ziman li ku tune |- | als || || Tosk Albanian || Albanian/Tosk || sq || ziman li ku tune |- | syr || || || Aramaic || arc || ziman li ku tune |- | ksh || || Kölsch || German || de || ziman li ku tune |- | pfl || || Palatine German || German || de || ziman li ku tune |- | kmr || || Northern Kurdish || Kurdish || ku || ziman li ku tune |- | fa-cls || || Classical Persian || Persian/Classical Persian || fa || ziman li ku tune |- | prs || || Dari || Persian/Dari || fa || ziman li ku tune |- | fa-ira || || Iranian Persian || Persian/Iranian Persian || fa || ziman li ku tune |} 3fy7rthnyl1safpgbhpoi7yrzh98z0z